23

W końcu mam mokre stopy z Dependency Injection (dawno spóźniony); Zacząłem grać z Unity i natrafiłem na problem ze wzorem strategii. Mogę użyć kontenera, aby wrócić do konkretnych implementacji strategii opartej na nazwie, ale nie widzę, jak mam uzyskać właściwą strategię w tym kontekście.
Zilustrujmy na prostym przykładzie: kontekst jest samochodem, który ma IEngine (strategię), z 2 implementacjami, FastEngine i SlowEngine. Kod wyglądałby wzdłuż tych linii:Wzorzec strategii i iniekcja zależności przy użyciu Unity

public interface IEngine 
{ 
    double MaxSpeed 
    { 
     get; 
    } 
} 

internal class FastEngine:IEngine 
{ 
    public double MaxSpeed 
    { 
     get 
     { 
      return 100d; 
     } 
    } 
} 

internal class SlowEngine:IEngine 
{ 
    public double MaxSpeed 
    { 
     get 
     { 
      return 10d; 
     } 
    } 
} 

public class Car 
{ 
    private IEngine engine; 
    public double MaximumSpeed 
    { 
     get 
     { 
      return this.engine.MaxSpeed; 
     } 
    } 

    public Car(IEngine engine) 
    { 
     this.engine = engine; 
    } 
} 

Mój problem jest następujący: w jaki sposób powinny przejść o uruchamianiu szybki samochód lub powolny samochód? Mogę użyć pojemnik dostarczyć mi każdej implementacji i mogę ustawić „domyślny” Wdrożenie używać:

IUnityContainer container = new UnityContainer(); 
container.RegisterType<IEngine, FastEngine>(); 
container.RegisterType<IEngine, FastEngine>("Fast"); 
container.RegisterType<IEngine, SlowEngine>("Slow"); 
var car = container.Resolve<Car>(); 
Assert.AreEqual(100, car.MaximumSpeed); 

ale co chciałbym to, aby móc zażądać samochodu z konkretnym wdrażania strategia - coś w rodzaju:

var car = container.Resolve<Car>(??? use "Fast" or "Slow ???); 

Czy mogę użyć pojemnika, aby to zrobić? Czy powinienem napisać fabrykę, która używa pojemnika? Wszelkie wskazówki będą mile widziane - nie jestem pewien, czy dobrze o tym myślę!

Odpowiedz

26

Typowy wzór w DI to w czasie wykonywania będzie tylko jedna implementacja danej abstrakcji. To sprawia, że ​​życie staje się o wiele łatwiejsze, ponieważ nie musisz zajmować się dwuznacznością, taką jak ta, którą opisujesz.

Czasami jednak trzeba zmienić implementację w zależności od kontekstu, na przykład podany przykład. Wiele kontenerów DI zapewnia sposoby, w których można podać parametr kwalifikujący, ale oznacza to, że w końcu ściśle powiązasz swój kod z konkretnym kontenerem DI.

Znacznie lepszym rozwiązaniem byłoby wprowadzenie modelu Abstract Factory, który może zapewnić to, czego potrzebujesz. Coś jak

public interface ICarFactory 
{ 
    Car Create(IEngine engine); 
} 

Jeśli trzeba wstrzyknąć więcej strategii, chyba wzór Builder konstrukcja może zmieścić nawet lepiej.

W każdym razie chodzi o to, że zamiast rejestrować wiele różnych samochodów w kontenerze, zamiast tego rejestruje się pojedynczą implementację ICarFactory.

W kodzie klienta można użyć wstrzykniętego ICarFactory do utworzenia instancji Car opartej na konkretnym IEngine.

var car = factory.Create(engine); 
+0

Dziękuję, wnikliwa odpowiedź. Używam wzorca strategii, z wieloma strategiami zamienionymi w czasie wykonywania, dużo; domyślnie robiłbym to, co opisujesz (Factory lub Builder), ale widziałem wzór strategii i wiele związanych DI, i chociaż to mogłoby pomóc. Z tego, co mówisz, wydaje się, że kontener byłby tylko nieznacznie pomocny. – Mathias

+0

Nadal uważam, że pojemniki są niezwykle pomocne. W takich przypadkach po prostu wstrzyknęliby fabrykę zamiast strategii, ale myślę, że nadal można zdecydować się na wdrożenie fabryki z pojemnikiem ... –

+4

Och, myślę, że rozumiem, co masz na myśli; zamiast zwrócić odpowiedni samochód, zwróć odpowiednią fabrykę w oparciu o silnik. W każdym razie twój komentarz re: kontenery używane do dostarczenia pojedynczej implementacji dla abstrakcji były bardzo pomocne; jest to zgodne z przykładami, które widziałem, które są zorientowane na konfigurację. W tej ramce możesz mieć wzorzec strategii, ale w konkretnym wdrożeniu będzie skonfigurowana tylko jedna implementacja. – Mathias