【问题标题】:Decorator pattern - Add methods to an interface装饰器模式 - 向接口添加方法
【发布时间】:2014-06-25 09:59:29
【问题描述】:

我目前正在努力实现 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();
}

有谁知道我该如何解决这个问题?

【问题讨论】:

  • 接口继承呢?
  • 我已经更新了我原来的问题以使用接口继承,但现在这意味着如果我需要一个存储库而不是 GetAll()、GetById()、Create() 那么我需要创建一个名为“DeleteCreateRepository”的类,它将实现 IDeleteRepository 和 ICreateRepository。你明白我的意思吗?
  • 为什么不使用扩展方法扩展接口?
  • 基本上,在存储库模式中,非常通用的接口应该具有所有基本方法,而不是可以由类实现或由其他接口继承。可以说, IRepository 是最通用的,然后 CustomerRepository 可以从 IRepository 继承所有常见操作。关于Unit of Work,一般会涉及到Save等提交逻辑。
  • 感谢您的信息。这正是我目前使用的方法。

标签: c# decorator


【解决方案1】:

我认为 KrishnaDhungana 建议的解决方案效果很好。这是我理解的一个例子,以简单的班级学生为例。很容易构建存储库并测试您是否注入到您的实现方法中......

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();
}   

【讨论】:

    【解决方案2】:

    在装饰器模式中,附加方法可能不会出现。装饰器模式意味着方法将保持不变,但行为可能会改变。我正在写一篇关于它的博客,一旦完成我会更新这个回复。

    以下代码可能会对您有所帮助,因为所有函数都可以轻松放入单个类中,并且您可以将其强制转换为相应的返回存储库。

    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()
        {
        }
    }
    

    【讨论】:

    • 仍在等待您的博文:D
    【解决方案3】:

    我认为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 Reference)

    // 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 会说它“已过时”,因此它并不完全正确,但它不会让该方法被调用)。

    【讨论】:

    • 我不喜欢这种方法,因为我有很多方法会抛出 NotSupportedException() 的存储库。
    • 您也可以/代替将它们标记为过时以实现“这行不通”的目标。扩展答案以符合要求。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-07-30
    • 2021-11-24
    • 2013-02-19
    • 2017-05-25
    • 2022-10-13
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多