Этот вопрос является результатом публикации Джеффри Палермо о том, как обойти разветвленный код и внедрение зависимостей http://jeffreypalermo.com/blog/constructor-over-injection-anti-pattern/
В своем посте у Джеффри есть класс (public class OrderProcessor : IOrderProcessor
), который принимает 2 интерфейса в конструкторе. Одним из них является валидатор IOrderValidator
и интерфейс IOrderShipper
. Его код метода ветвится только после использования методов в интерфейсе IOrderValidator
и никогда не использует ничего в интерфейсе IOrderShipper
.
Он предлагает создать фабрику, которая будет вызывать статический метод для получения делегата интерфейса. В своем рефакторированном коде он создает новый объект, который кажется ненужным.
Я думаю, суть проблемы в том, что мы используем IoC для создания всех наших объектов независимо от того, используются они или нет. Если вы создаете экземпляр объекта с 2 интерфейсами и имеете код, который может переходить, чтобы не использовать один из них, как вы справитесь с этим?
В этом примере мы предполагаем, что _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
}
}
А вот метод, который настраивает эту фабрику во время запуска (global.asax для ASP.NET):
private static void ConfigureFactories()
{
OrderShipperFactory.CreationClosure =
() => ObjectFactory.GetInstance<IOrderShipper>();
}