Шаблон декоратора - Добавить методы в интерфейс

В настоящее время я борюсь за реализацию IUnitOfwork. Давайте предположим, что у меня есть интерфейс, который имеет 2 метода:

public interface IRepository<TEntity, in TKey>
{

    TEntity Get(TKey id);

    IQueryable<TEntity> All();
}

Теперь предположим, что у меня есть пара классов (сущностей), которые реализуют этот интерфейс. Но, возможно, что некоторые сущности должны иметь дополнительные методы для запроса, такие как GetById(int id).

Эту проблему легко решить, создав новый интерфейс IRepositoryWithGetById.

public interface IRepository<TEntity, in TKey>
{

    TEntity Get(TKey id);

    TEntity GetById(int id);

    IQueryable<TEntity> All();
}

Вы будете, к чему это ведет, кошмаром для поддержки кода. Я думал об использовании шаблона декоратора, но я не совсем нашел хорошее решение.

Примечание: я использую интерфейсы, так как я должен быть в состоянии сделать это.

По предложению пользователя я использую наследование интерфейса, поэтому вот обновленный код:

public class Wrapper
{
    public IRepository standardRepository = new Repository();
    public IDeleteRepository deleteRepository = new DeleteRepository();
    public ICreateRepository createRepository = new CreateRepository();    
}

public class Repository : IRepository
{
    public void GetAll() { }
    public void GetById(int id) { }
}

public class DeleteRepository : Repository, IDeleteRepository
{
    public void Delete() { }
}

public class CreateRepository : Repository, ICreateRepository
{
    public void Create() { }
}

public interface IRepository
{
    void GetAll();
    void GetById(int id);
}

public interface IDeleteRepository : IRepository
{
    void Delete();
}

public interface ICreateRepository : IRepository
{
    void Create();
}

Кто-нибудь, кто знает, как я могу решить эту проблему?

3 ответа

Решение

Я думаю, что решение, предложенное КришнаДунгана, работает отлично. Вот пример, как я понимаю, на примере простого ученика. Репозитории легко создавать и проверять, внедряете ли вы в свой метод реализации...

public class Student
{
    int NoOfParties;
    int NoOfHangOvers;
}

public interface IRepo<T>
{
    IEnumerable<T> GetAll();
    T GetByID();
}

public interface IRepoCreate<T>
{
    Int32 Create();
}

public interface IRepoDelete<T>
{
    void Delete();
}

public interface IStudentRepo : IRepo<Student>, IRepoCreate<Student>, IRepoDelete<Student>
{
    IEnumerable<Student> GetAll();
    Student GetByID();
    int Create();
    void Delete();
    Student GetByParty();
}

public class MSSQLStudentRepo : IStudentRepo
{
    public IEnumerable<Student> GetAll() { \\stuff }
    public Student GetByID() { \\stuff }
    public int Create() { \\stuff }
    public void Delete() { \\stuff }
    public Student GetByParty() { \\stuff }
} 

public class MySQLStudentRepo : IStudentRepo
{
    public IEnumerable<Student> GetAll() { \\stuff }
    public Student GetByID() { \\stuff }
    public int Create() { \\stuff }
    public void Delete() { \\stuff }
    public Student GetByParty() { \\stuff }
}

public void ImplementationExample()
{
    IStudentRepo Repo = new MSSQLStudentRepo();
    var Bob = Repo.GetByParty();
}   

В шаблоне декоратора дополнительные методы могут не отображаться. Шаблон декоратора означает, что методы останутся такими же, но поведение может измениться. Я пишу на нем блог, и я обновлю этот ответ, как только сделаю.

Следующий код может помочь вам, так как все функции могут легко поместиться в класс sigle, и вы можете привести его к соответствующему репозиторию возврата.

using System.Linq;

public interface IRepository<TEntity, in TKey>
{

    TEntity Get(TKey id);

    IQueryable<TEntity> All();
}

public interface IRepository
{
    void GetAll();
    void GetById(int id);
}

public interface IDeleteRepository : IRepository
{
    void Delete();
}

public interface ICreateRepository : IRepository
{
    void Create();
}


public class Wrapper
{
    private readonly CombinedRepository standardRepository = new CombinedRepository();

    public IRepository Repository
    {
        get { return standardRepository; }
    }

    public ICreateRepository CreateRepository
    {
        get { return standardRepository; }
    }

    public IDeleteRepository DeleteRepository
    {
        get { return standardRepository; }
    }
}

public class CombinedRepository : IRepository, IDeleteRepository, ICreateRepository
{
    public void GetAll()
    {
    }

    public void GetById(int id)
    {
    }

    public void Delete()
    {
    }

    public void Create()
    {
    }
}

Я думаю abstract классы работают довольно хорошо для репозиториев, которые нужно высмеивать, с virtual реализации, которые могут быть переопределены при необходимости.

public interface IRepository<TEntity, in TKey>
{
    TEntity Get(TKey id);
    TEntity GetById(int id);
    IQueryable<TEntity> All();
}

public abstract class Repository : IRepository
{
    public virtual TEntity Get(TKey id) { throw new NotImplementedException(); }
    public virtual TEntity GetById(int id) { throw new NotImplementedException(); }
    public virtual IQueryable<TEntity> All() { throw new NotImplementedException(); }
}

public class FullRepo : Repository
{
    public virtual TEntity Get(TKey id) { /*Implement it!*/ }
    public virtual TEntity GetById(int id) { /*Implement it!*/ }
    public virtual IQueryable<TEntity> All() { /*Implement it!*/ }
}

// No GetById here
public class PartialRepo : Repository
{
    public virtual TEntity Get(TKey id) { /*Implement it!*/ }
    public virtual TEntity GetById(int id) { throw new NotSupportedException(); }
    public virtual IQueryable<TEntity> All() { /*Implement it!*/ }
}

По сути, реализуйте только то, что вам нужно, с интерфейсом / абстрактным классом, представляющим все. Этот подход (по крайней мере для меня) очень читабелен и легко расширяется... и очень насмешлив.

Чтобы избежать броска и вместо того, чтобы запретить использование методов, вы можете использовать ObsoleteAttribute ( Справочник MSDN)

// No GetById here
public class PartialRepo : Repository
{
    public virtual TEntity Get(TKey id) { /*Implement it!*/ }
    [Obsolete("This method can't be used by this repository", true)]
    public virtual TEntity GetById(int id) { /*Can be empty*/ }
    public virtual IQueryable<TEntity> All() { /*Implement it!*/ }
}

Переходя true делает вызов метода ошибкой компиляции (VS скажет, что он "устарел", поэтому он не будет абсолютно правильным, но, тем не менее, не позволит методу быть вызванным).

Другие вопросы по тегам