Тот факт, что в DI-коде вручную используется множество стандартных кодов для иерархий классов, подобных этому, объясняет, почему существуют платформы.Извините, но если вы не можете заставить того, кто решил по правилу no DI / IoC, изменить свое мнение, вы либо будете писать множество шаблонного кода, либо закончите писать фреймворк самостоятельно.
РЕДАКТИРОВАТЬ - с полностью вымышленной структурой, чтобы сохранить это как можно более независимым, но объясняя, как можно исключить все, кроме одного вызова в контейнер во многих сценариях.
Итак, с реализациейиз Translator
вроде:
public class Translator
{
private ITranslator translatorInstance;
public Translator()
{
SomeContainer container = SomeContainer.CreateFromConfig(configFilePath);
// this is the ONLY point we touch the container
translatorInstance = container.GetMeA<ITranslator>();
}
// implementation
}
Мы можем видеть, что это работает как фабрика и является единственным классом, который должен знать о самом контейнере.Следовательно, реализация одного конкретного разработчика ITranslator
может быть такой:
public class FileTranslator : ITranslator
{
// private fields
public FileTranslator( ISectionATranslator sectionAtrans,
ISectionBTranslator sectionBtrans,
ISectionCTranslator sectionCtrans)
{
this.sectionAtrans = sectionAtrans;
// etc
}
// implementation
}
Обратите внимание, что FileTranslator
ничего не знает о том, от каких конкретных классов на самом деле реализуются интерфейсы, от которых оно зависит, и не нуждается ни в каком виде.завод.На самом деле, контейнер сделает это за вас.Есть несколько способов, которыми контейнеры справляются с этим, один пример - явная конфигурация, например:
<!-- absolutely fictitious configuration file, but similar to many frameworks -->
<ContainerConfig>
<ObjectResolver interface="ITranslator">
<ConcreteType type="FileTranslator">
<ConstructorInjection>
<Argument ordinal="0" type="SectionATranslator" />
<Argument ordinal="1" type="SectionBTranslator" />
<Argument ordinal="2" type="SectionCTranslator" />
</ConstructorInjection>
</ConcreteType>
</ObjectResolver>
</ContainerConfig>
Многие фреймворки даже не нуждаются в том, чтобы вы определяли конкретные аргументы конструктора, вы можете просто заявить, что если выхотите получить ISectionATranslator
, а затем вернуть SectionATranslator
, и он автоматически создаст их перед вызовом конструктора.
Также обратите внимание, что некоторые платформы предоставляют возможность определять эти правила разрешения типов в коде, используя API-интерфейсы в свободном стиле,а некоторые позволяют вам определить несколько возможных способов разрешения определенного типа с помощью некоторого имени (возможно, реализация "Production"
по сравнению с реализацией "UnitTest"
).
Обратите внимание, что я оставил вышеизложенное намеренно расплывчатым, потому что яне хотите говорить, какая платформа является лучшей (и, честно говоря, я думаю, что это зависит от ваших индивидуальных потребностей) - проверьте в других местах StackOverflow для сравнения структур, и, пожалуйста, попробуйте несколько (возможно, вы можете попробовать некоторые, не сказав своему боссу!).Надеемся, однако, что вышеприведенное показывает, почему контейнер IoC может сделать ваш код намного чище, устраняя необходимость в слоях на слоях фабричных классов.