2009-12-18 6 views
44

Po prostu załóżmy, że mam klasę Foo, która ma dwie zależności: ISerializer<T> i IFileAccessHandler.Nie można połączyć Factory/DI

Ta klasa ma również inne zależności, zależności funkcjonalne. Nie chcę, aby ktokolwiek tworzył tę klasę w niepoprawnym stanie, więc musiałbym przekazać obiekt domeny w konstruktorze.

Ale jak mogę to załatwić przez IoC, gdy wiem również, jaki obiekt domeny przekazać w momencie, w którym faktycznie tworzę klasę Foo?

Ustawiłem obiekt domeny jako własność ustawioną przez fabrykę. Dlatego Factory wykonuje wywołanie Service Locator, aby uzyskać prawidłowo utworzoną klasę "Foo" z jej zależnościami, a następnie wypełnia ją właściwym obiektem domeny i zwraca go.

Ale czy to najlepszy sposób, aby przejść? Wolałbym, aby obiekt domeny był częścią mojego konstruktora, aby był on faktycznie zgodny z "Foo".

Wszelkie pomysły? Czy tu czegoś brakuje?

Odpowiedz

66

Domyślne rozwiązanie DI, gdy nie można podłączyć się konkretny typ w czasie rejestracji jest użycie Abstract Factory

W twoim przypadku, chciałbym zdefiniować interfejs IFooFactory:

public interface IFooFactory 
{ 
    Foo Create(DomainClass dc); 
} 

Pozwoli to na zdefiniowanie konkretnej implementacji, która będzie świadczyć o twoich usługach infrastrukturalnych.

public class FooFactory : IFooFactory 
{ 
    private readonly ISerializer serializer; 
    private readonly IFileAccessHandler fileHandler; 

    public FooFactory(ISerializer serializer, IFileAccessHandler fileHandler) 
    { 
     if(serializer == null) 
     { 
      throw new ArgumentNullException("serializer"); 
     } 
     if(fileHandler == null) 
     { 
      throw new ArgumentNullException("fileHandler"); 
     } 

     this.serializer = serializer; 
     this.fileHandler = fileHandler; 
    } 

    public Foo Create(DomainClass dc) 
    { 
     return new Foo(this.serializer, this.fileHandler, dc); 
    } 
} 

W ten sposób można chronić niezmienników swojej klasie Foo, co pozwala na pobyt z Constructor wtrysk.

W kontenerze DI można zarejestrować IFooFactory i odpowiednią implementację. Wszędzie, gdzie masz instancję DomainClass i potrzebujesz instancji Foo, będziesz wtedy musiał polegać na IFooFactory i używać go.

+0

Dlaczego tworzysz Foo ręcznie? W takim przypadku FooFactory ma niepotrzebne zależności. Dlaczego nie używasz container.Resolve (dc) zamiast nowego Foo (this.serializer, this.fileHandler, dc)? –

+12

Ponieważ byłby to anty-wzór usługi Locator (http://blog.ploeh.dk/2010/02/03/ServiceLocatorIsAnAntiPattern.aspx).Kontener DI nie może zaatakować reszty aplikacji. Jednak niektóre kontenery (np. Windsor) mogą automatycznie implementować i emitować implementacje abstrakcyjnych fabryk, w którym to przypadku FooFactory będzie całkowicie nadmiarowy. Jeśli chcesz korzystać z pojemnika DI, przejdź całą drogę :) –

+1

Zgadzam się, że usługa Lokalizator ma charakter zapobiegawczy, ale każda reguła ma wyjątki. Nie powinieneś stosować się do tej reguły na ślepo! Drutujesz swoje obiekty ręcznie, twoja abstrakcyjna fabryka ma nieprawidłowe zależności. To zapach złego projektu, prawda? Pojemnik DI ma na celu pozbycie się tego zapachu, prawda? Użyj kontenera DI, aby utworzyć Foo! Masz rację :) Jeśli chcesz korzystać z pojemnika DI, przejdź całą drogę :) –

0

Ja także zmagam się z tym problemem. Przykład Marka jest ograniczony tym, że FooFactory tworzy konkretną klasę Foo. Co się stanie, jeśli powstanie IFoo, w którym implementacja zostanie określona podczas konfiguracji startowej? Sugerowałoby to dla każdej alternatywnej implementacji IFoo (FooA, FooB itp.) Potrzebowałbyś konkretnej implementacji odpowiedniej fabryki (FooAFactory, FooBFactory, itp.). To mnie uderza jako zbyteczne.

Jeśli fabryka jest zdefiniowana na tym samym poziomie co implementacja i inicjalizacja kontenera, nie widzę słabości odniesienia kontenera przez fabrykę. Nadal utrzymuje odniesienia do pojemnika z wycieku do pozostałej części aplikacji.

Pozdrawiamy,

Metro.

+0

jest post, który zgadza się z tym punktem widzenia (mark^napisał to), ale wtedy każdy konsument musiałby wdrożyć własną fabrykę. (po prostu dodaj nie dobry lub zły do ​​twojego posta) [http://blog.ploeh.dk/2012/03/15/ImplementinganAbstractFactory/] – workabyte

+0

@Metro Popraw mnie, jeśli nie czytam poprawnie twojej propozycji. Więc twoją propozycją jest, aby fabryka rozwiązała abstrakcyjny typ za pomocą kontenera? Czyli zasadniczo Service Locator? Ale połączenie z serwisem, który nie zaszkodzi twojej duszy, skoro wdrożenie fabryki jest na tym samym poziomie co kontener IoC? – Ash