介绍
大家好,我正在使用C#编写一个持久化库。在这个库里,我实现了仓储模式,但是我遇到了SOLID原则的问题。下面是一个简化版的当前实现,以便集中讨论关键问题:
在持久化库中包含的抽象仓储:
public abstract class Repository<T>
{
protected Repository(
IServiceA serviceA,
IServiceB serviceB)
{
/* ... */
}
}
由库用户创建的具体代码仓库:
public class FooRepository : Repository<Foo>
{
protected FooRepository(
IServiceA serviceA,
IServiceB serviceB) :
base(serviceA, serviceB)
{
/* ... */
}
}
问题
目前的代码,派生类必须知道基类的每一个依赖关系,这可能没问题,但是如果我添加了基类的依赖项呢?每个派生类都会出现问题,因为它们需要将新的依赖项传递给基类...所以目前,我不能改变基类构造函数,这是一个问题,因为我希望我的基类具有进化的可能性。这种实现明显违反了开闭原则,但是我不知道如何解决这个问题而不破坏SOLID...
要求
- 库对用户使用应该简单易懂
- 可以通过DI来构建具体的仓库
- 应该能够在抽象仓库中添加一个或多个依赖项,而不影响派生仓库
- 应该可以使用命名约定在DI容器中注册每个仓库,就像ASP.NET MVC框架中的控制器一样
- 用户应该能够在其派生仓库中添加更多依赖项(如果他愿意)
已经考虑的解决方案
1. 服务聚合器模式
根据这篇文章,服务聚合器模式可以应用于这种情况,因此代码将如下所示:
包含在持久性库中的抽象仓库:
public abstract class Repository<T>
{
public interface IRepositoryDependencies
{
IServiceA { get; }
IServiceB { get; }
}
protected Repository(IRepositoryDependencies dependencies)
{
/* ... */
}
}
由库用户创建的具体存储库:
public class FooRepository : Repository<Foo>
{
protected Repository(IRepositoryDependencies dependencies) :
base(dependencies)
{
/* ... */
}
}
优点
- 如果基类添加一个依赖项,派生类不会被破坏。
缺点
- 如果我们添加一个依赖关系,则必须修改“IRepositoryDependencies”接口的实现。
- 该文章没有解释如何使用“Castle DynamicProxy2”(这对我来说是一种未知技术)来动态生成服务聚合器。
2. 建造者模式
也许可以删除基本存储库构造函数并引入建造者模板来创建存储库,但要使此解决方案工作,建造者必须是可继承的,以允许用户输入自己的存储库依赖项。
优点
- 如果基类添加一个依赖项,派生类不会被破坏。
- 存储库构建由另一个类管理。
缺点
- 用户必须为他想要创建的每个存储库创建一个构建器。
- 使用命名约定通过DI注册每个存储库变得更加困难。
3. 属性注入
也许可以取消基本存储库构造函数并配置DI使用属性注入。
优点
- 如果基类添加一个依赖项,派生类不会被破坏。
缺点
- 我认为属性设置器必须是公共的吗?
结论
在SOLID世界中,是否有任何提到的解决方案是可接受的?如果没有,你们有什么解决方法?非常感谢您的帮助!