这个问题是Jeffery Palermo关于如何解决分支代码和依赖注入问题的结果http://jeffreypalermo.com/blog/constructor-over-injection-anti-pattern/
在他的帖子中,Jeffery有一个类(public class OrderProcessor : IOrderProcessor
),它在构造函数上有2个接口。一个是验证器IOrderValidator
和IOrderShipper
接口。他的方法代码仅在IOrderValidator
接口上使用方法后才会分支,并且从不在IOrderShipper
接口上使用任何内容。
他建议创建一个工厂,调用静态方法来获取接口的委托。他正在重构的代码中创建一个新对象,这似乎是不必要的。
我想这个问题的症结在于我们正在使用IoC来构建我们所有的对象,无论它们是否被使用。 如果您实例化一个具有2个接口的对象,并且代码可以分支为不使用其中一个,那么您如何处理它?</ strong>
在此示例中,我们假设_validator.Validate(order)
始终返回false,并且永远不会调用IOrderShipper.Ship()
方法。
原始代码:
public class OrderProcessor : IOrderProcessor
{
private readonly IOrderValidator _validator;
private readonly IOrderShipper _shipper;
public OrderProcessor(IOrderValidator validator, IOrderShipper shipper)
{
_validator = validator;
_shipper = shipper;
}
public SuccessResult Process(Order order)
{
bool isValid = _validator.Validate(order);
if (isValid)
{
_shipper.Ship(order);
}
return CreateStatus(isValid);
}
private SuccessResult CreateStatus(bool isValid)
{
return isValid ? SuccessResult.Success : SuccessResult.Failed;
}
}
public class OrderShipper : IOrderShipper
{
public OrderShipper()
{
Thread.Sleep(TimeSpan.FromMilliseconds(777));
}
public void Ship(Order order)
{
//ship the order
}
}
重构代码
public class OrderProcessor : IOrderProcessor
{
private readonly IOrderValidator _validator;
public OrderProcessor(IOrderValidator validator)
{
_validator = validator;
}
public SuccessResult Process(Order order)
{
bool isValid = _validator.Validate(order);
if (isValid)
{
IOrderShipper shipper = new OrderShipperFactory().GetDefault();
shipper.Ship(order);
}
return CreateStatus(isValid);
}
private SuccessResult CreateStatus(bool isValid)
{
return isValid ? SuccessResult.Success : SuccessResult.Failed;
}
}
public class OrderShipperFactory
{
public static Func<IOrderShipper> CreationClosure;
public IOrderShipper GetDefault()
{
return CreationClosure(); //executes closure
}
}
以下是在启动时配置此工厂的方法(ASP.NET的global.asax):
private static void ConfigureFactories()
{
OrderShipperFactory.CreationClosure =
() => ObjectFactory.GetInstance<IOrderShipper>();
}
答案 0 :(得分:22)
我刚刚发布了rebuttal of Jeffrey Palermos post。
简而言之,我们不应该让具体的实施细节影响我们的设计。这将违反建筑规模上的Liskov替代原则。
更优雅的解决方案让我们通过引入延迟加载的OrderShipper来保持设计。
答案 1 :(得分:1)
我参加会议的时间很晚,但有几个快点...
坚持只使用一个依赖关系的代码分支,有两个分支建议:
与此处提到的其他人一样,重点是打破对具体类的依赖,并使用依赖注入将依赖项与使用中的某个IoC容器松散耦合。
通过限制未来发生的技术债务,这使得未来的重构和交换遗留代码变得更加容易。一旦你有一个项目接近500,000行代码,有3000个单元测试,你就会知道为什么IoC如此重要。
答案 2 :(得分:-2)
我认为这里的反模式是过度使用接口。