На самом деле, «правильный» способ - НЕ использовать фабрику вообще, если нет абсолютно никакого другого выбора (как в модульном тестировании и некоторых моделях - для производственного кода вы НЕ используете фабрику)! Это на самом деле анти-паттерн и его следует избегать любой ценой. Весь смысл в контейнере DI состоит в том, чтобы позволить гаджету сделать всю работу за вас.
Как было сказано выше в предыдущем посте, вы хотите, чтобы ваш гаджет IoC взял на себя ответственность за создание различных зависимых объектов в вашем приложении. Это означает, что ваш DI-гаджет может создавать различные экземпляры и управлять ими. В этом весь смысл DI - ваши объекты НИКОГДА не должны знать, как создавать и / или управлять объектами, от которых они зависят. В противном случае нарушается ослабление сцепления.
Преобразование существующего приложения во все DI является огромным шагом, но, исключая очевидные трудности в этом, вы также захотите (просто чтобы сделать вашу жизнь немного проще) изучить инструмент DI, который будет автоматически выполнять большую часть ваших привязок. (ядро чего-то вроде Ninject - это "kernel.Bind<someInterface>().To<someConcreteClass>()"
вызовы, которые вы делаете для сопоставления объявлений интерфейсов с теми конкретными классами, которые вы хотите использовать для реализации этих интерфейсов. Это те вызовы «Bind», которые позволяют гаджету DI перехватывать вызовы конструктора и предоставлять необходимые экземпляры зависимых объектов. Типичный конструктор (показанный здесь псевдокод) для некоторого класса может быть:
public class SomeClass
{
private ISomeClassA _ClassA;
private ISomeOtherClassB _ClassB;
public SomeClass(ISomeClassA aInstanceOfA, ISomeOtherClassB aInstanceOfB)
{
if (aInstanceOfA == null)
throw new NullArgumentException();
if (aInstanceOfB == null)
throw new NullArgumentException();
_ClassA = aInstanceOfA;
_ClassB = aInstanceOfB;
}
public void DoSomething()
{
_ClassA.PerformSomeAction();
_ClassB.PerformSomeOtherActionUsingTheInstanceOfClassA(_ClassA);
}
}
Обратите внимание, что нигде в этом коде не было никакого кода, который создавал / управлял / выпускал экземпляр SomeConcreteClassA или SomeOtherConcreteClassB. На самом деле ни на один конкретный класс даже не ссылались. Итак ... где случилось волшебство?
В начальной части вашего приложения произошло следующее (опять же, это псевдокод, но он довольно близок к реальному (Ninject) ...):
public void StartUp()
{
kernel.Bind<ISomeClassA>().To<SomeConcreteClassA>();
kernel.Bind<ISomeOtherClassB>().To<SomeOtherConcreteClassB>();
}
Этот небольшой фрагмент кода говорит гаджету Ninject искать конструкторы, сканировать их, искать экземпляры интерфейсов, для которых он был настроен (это вызовы «Bind»), а затем создавать и заменять экземпляр конкретного класса везде, где это необходимо. на экземпляр ссылаются.
Есть хороший инструмент, который очень хорошо дополняет Ninject, который называется Ninject.Extensions.Conventions (еще один пакет NuGet), который сделает большую часть этой работы за вас. Не для того, чтобы отвлечься от отличного опыта обучения, через который вы будете проходить, создавая его самостоятельно, но для начала, это может быть инструментом для исследования.
Если память служит, Unity (формально от Microsoft, теперь проект с открытым исходным кодом) имеет вызов метода или два, которые делают то же самое, другие инструменты имеют аналогичные помощники.
Какой бы путь вы ни выбрали, обязательно прочитайте книгу Марка Симанна для большей части вашего обучения DI, однако, следует отметить, что даже «Великие» в мире разработки программного обеспечения (такие как Марк) могут совершать явные ошибки - Марк забыл все о Ninject в своей книге, так что вот еще один ресурс, написанный только для Ninject. У меня есть это и его хорошее чтение: Освоение Ninject для инъекций зависимости