很好。让我们有一个基类 BaseService 和一个构造函数,将其依赖项作为参数传递:
public abstract class BaseService
{
protected IFoo Foo { get; private set; }
protected IBar Bar { get; private set; }
public BaseService(IFoo foo, IBar bar)
{
Foo = foo;
Bar = bar;
}
}
public class Service : BaseService
{
protected IOtherDependency otherDependency { get; private set; }
public Service(IOtherDependency otherDependency, IFoo foo, IBar bar)
: base(foo, bar)
{
OtherDependency = otherDependency;
}
}
BaseService 类的开发人员现在发现应该将其某些功能外包给该类应该依赖的外部服务时该怎么办?
在BaseService类构造函数中添加另一个INewDependency参数违反了与派生类开发者的约定,因为他们显式调用BaseService类构造函数并且只期望两个参数,所以当升级到新的版本,找不到对应的签名构造函数,编译失败。在我看来,在派生类的代码中重复基类依赖列表的要求是违反了Single Source Of Truth原则,将部分基类功能分配给依赖后编译失败是由于这种违规行为。
作为一种变通方法,我建议将基类的所有依赖集中到指定密封类的属性中,通过ConfigureServices注册这个类,并在构造函数参数中传递。
对派生类执行相同的操作。开发人员使用 IServicesCollection 注册这些类中的每一个。
public abstract class BaseService
{
protected IFoo Foo { get; private set; }
protected IBar Bar { get; private set; }
public BaseService(Dependencies dependencies)
{
Foo = dependencies.Foo;
Bar = dependencies.Bar;
}
public sealed class Dependencies
{
public IFoo Foo { get; private set; }
public IBar Bar { get; private set; }
public Dependencies(IFoo foo, IBar bar)
{
Foo = foo;
Bar = bar;
}
}
}
具有父类依赖关系的对象将由提供子类依赖关系的类属性引用。但是,派生类的代码完全是从父类的依赖列表中抽象出来的,封装在BaseService.Dependencies类型中:
public class Service : BaseService
{
protected IOtherDependency OtherDependency { get; private set; }
public Service(Dependencies dependencies) : base(dependencies.BaseDependencies)
{
OtherDependency = dependencies.OtherDependency;
}
public sealed class Dependencies
{
public IOtherDependency OtherDependency { get; private set; }
public BaseService.Depencencies BaseDependencies { get; private set; }
public Dependencies(IOtherDependency otherDependency, BaseService.Dependencies baseDependencies)
{
OtherDependency = otherDependency;
BaseDependencies = baseDependencies;
}
}
}
在这个设计中,每个类的构造函数都有一个参数,一个密封类的实例及其依赖项,继承的依赖项作为属性传递。类依赖列表封装在Dependencies 类中,与类和默认IServiceCollection 注册一起提供给消费者。
如果BaseClass 开发人员决定将某些功能外包给新的依赖项,则所有必要的更改都将在提供的包中进行,而其使用者无需更改其代码中的任何内容。