Właściwie „właściwym” sposobem jest wcale NIE używanie fabryki, chyba że absolutnie nie ma innego wyboru (jak w testach jednostkowych i niektórych próbach - dla kodu produkcyjnego NIE używasz fabryki)! Jest to w rzeczywistości anty-wzór i należy go za wszelką cenę unikać. Istotą kontenera DI jest umożliwienie gadżetowi wykonania pracy za Ciebie.
Jak stwierdzono powyżej w poprzednim poście, chcesz, aby Twój gadżet IoC przejął odpowiedzialność za tworzenie różnych zależnych obiektów w Twojej aplikacji. Oznacza to, że gadżet DI może samodzielnie tworzyć różne instancje i zarządzać nimi. To jest cały punkt za DI - twoje obiekty NIGDY nie powinny wiedzieć, jak tworzyć i / lub zarządzać obiektami, na których polegają. W przeciwnym razie zrywa luźne połączenie.
Konwersja istniejącej aplikacji na wszystkie DI jest ogromnym krokiem, ale pomijając oczywiste trudności w zrobieniu tego, będziesz również chciał (tylko żeby twoje życie było trochę łatwiejsze) zbadać narzędzie DI, które automatycznie wykona większość twoich powiązań (rdzeń czegoś takiego jak Ninject to "kernel.Bind<someInterface>().To<someConcreteClass>()"
wywołania, które wykonujesz w celu dopasowania deklaracji interfejsu do tych konkretnych klas, których chcesz użyć do implementacji tych interfejsów. To te wywołania „Bind”, które pozwalają Twojemu gadżetowi DI przechwytywać wywołania konstruktora i zapewniać niezbędne instancje obiektów zależnych. Typowym konstruktorem (pokazany tutaj pseudo kod) dla niektórych klas może być:
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);
}
}
Zauważ, że nigdzie w tym kodzie nie było żadnego kodu, który utworzyłby / zarządzał / wydał ani wystąpienie SomeConcreteClassA lub SomeOtherConcreteClassB. W rzeczywistości żadna konkretna klasa nie została nawet przywołana. Więc ... gdzie stała się magia?
W części początkowej aplikacji miały miejsce następujące zdarzenia (znowu jest to pseudo kod, ale jest bardzo zbliżony do rzeczywistej (Ninject) rzeczy ...):
public void StartUp()
{
kernel.Bind<ISomeClassA>().To<SomeConcreteClassA>();
kernel.Bind<ISomeOtherClassB>().To<SomeOtherConcreteClassB>();
}
Ta odrobina kodu mówi gadżetowi Ninject, aby szukał konstruktorów, skanował je, szukał instancji interfejsów, które zostały skonfigurowane do obsługi (to są wywołania „Bind”), a następnie tworzył i zastępował instancję konkretnej klasy, gdziekolwiek instancja jest przywoływana.
Istnieje ładne narzędzie, które bardzo dobrze uzupełnia Ninject o nazwie Ninject.Extensions.Conventions (kolejny pakiet NuGet), które wykona większość tej pracy za Ciebie. Nie odrywając się od doskonałych doświadczeń edukacyjnych, przez które przechodzisz, gdy sam to budujesz, ale aby zacząć, może to być narzędzie do zbadania.
Jeśli pamięć służy, Unity (formalnie Microsoft jest teraz projektem Open Source) ma wywołanie metody lub dwie, które robią to samo, inne narzędzia mają podobne pomocniki.
Niezależnie od tego, którą ścieżkę wybierzesz, zdecydowanie przeczytaj książkę Marka Seemanna, aby uzyskać większą część szkolenia DI, jednak należy zauważyć, że nawet „wielcy” świata inżynierii oprogramowania (jak Mark) mogą popełniać rażące błędy - Mark zapomniał o wszystkim Ninject w swojej książce, więc oto kolejny zasób napisany tylko dla Ninject. Mam go i jest to dobra lektura: Mastering Ninject for Dependency Injection