2011-08-13 14 views
5

Staram się polepszyć z IoC, DI i OOD dla lepszej testowalności i luźniejszego sprzężenia.OOD przy użyciu kontenerów IoC - jak konstruować obiekty zależne?

Kiedy więc zaprojektować zajęcia z wykorzystaniem ciężkiego IoC oraz DI możemy endup z klas z wieloma zależnościami np

class Processor 
{ 
    private IService1 m_Service1; 
    private IService2 m_Service2; 
    private IService3 m_Service3; 

    //approach 1 
    public Processor(IService1 service1, IService2 service2, IService3 service3) 
    { 
     m_Service1 = service1; 
     m_Service2 = service2; 
     m_Service3 = service3; 
    } 
    //approach 2 
    public Processor(IContainer container) 
    { 
     m_Service1 = container.Resolve<IService1>(); 
     m_Service2 = container.Resolve<IService2>(); 
     m_Service3 = container.Resolve<IService3>(); 
    } 

    //approach 3 
    public delegate Processor Factory(); 

} 

Im myślenia, co powinno być zwykłe podejście tutaj. Możemy zostawić konstruktora z 3 parametrów, ale jeśli mamy aplikację za pomocą autofac (na przykład) najprawdopodobniej budowy będzie rzadko być wykorzystywane do innych celów niż rozdzielając typy od jakiegoś przykład pojemnika jak

Processor proc = new Processor(
container.Resolve<IService1>(), 
container.Resolve<IService2>(), 
container.Resolve<IService3>()); 

tak myślę może podejście 2 jest lepsze, gdy zależy nam na wielu typach z kontenera. W każdym razie będziemy musieli dodać gdzieś odniesienie do autofac, więc czy nie ma powodów, żeby tego nie robić?

Autofac zapewnia także metoda fabryki delegat podejście

http://code.google.com/p/autofac/wiki/DelegateFactories

var processorFactory = container.Resolve<Processor.Factory>(); 
Processor processor = processorFactory.Invoke(); 

Więc mamy również zbliżyć 3 - nie będziemy używać konstruktorów do stworzenia naszych wystąpień klasy, zamiast będziemy nazywając rozwiązany delegata z pojemnikiem i rozwiąże dla nas zależności.

Od czasu, gdy jestem całkiem nowy w IoC, trudno powiedzieć, kiedy powinniśmy użyć 1,2,3. Mają zalety i wady.

Ogólnie uważam, że jeśli klasa ma 1 zależność, prawdopodobnie zawsze możemy zastosować podejście 1 .. poza tym, że nie jestem pewien, co wybrać i kiedy.

UPDATE czytałem o lokalizatora usług anty wzór ale Ive wymyślić 4 (lub prawdziwego 3 podejścia)

jej pobliżu ServiceLocator wyjątkiem jej nie mijamy obiekt, który wygląda tak

public class ServiceLocatorObject 
{ 
     private IService1 m_Service1; 
     private IService2 m_Service2; 
     private IService3 m_Service3; 
     public IService1 Service1 {get {return m_Service1;}} 
     public IService2 Service2 {get {return m_Service2;}} 
     public IService3 Service3 {get {return m_Service3;}} 

     public ServiceLocatorObject(IService1 service1, IService2 service2, IService3 service3) 
     { 
      m_Service1 = service1; 
      m_Service2 = service2; 
      m_Service3 = service3; 
     } 
} 

A teraz tworzymy

//approach 4 
public Processor(ServiceLocatorObject servicesToUse) 
{ 
    m_Services = servicesToUse; 
} 

teraz mamy naszą klasę oddzielone od implementacji usługowym d jego jasne, jakie rzeczywiste zależności potrzebuje (jeśli założymy, że wszystkie usługi dostępne na przekażonym obiekcie są wymagane), ponieważ nie przekazujemy kontenera, który może zawierać 100 implementacji. Obiekt ten może być nawet ponownie wykorzystany, jeśli ta 3 kombinacja usług może być wymagana w innej klasie w naszej aplikacji. Używamy więc wzorca konstruktora DI nie ServiceLocator. Interfejs jest przejrzysty i nie jest przeładowany zależnościami, nowa klasa może być dobrym kandydatem do ponownego wykorzystania.

Co byś powiedział na ten temat?

Odpowiedz

6

Wzór lokalizacji usługi jest często uważany za wzór antyadhezyjny w dzisiejszych czasach (za pomocą kontenera. Rozpuścić i wstrzyknąć pojemnik).

Po tym, jak DUŻO walcząc z tym pojęciem i próbując zdecydować, czy mi się to podoba, czy nienawidzę, doszedłem do osobistej świadomości, że zgadzam się, że lokalizacja usługi jest anty-wzorcem - ponieważ zaciemnia ona współzależności, które istnieją i które są podstawową koncepcją OOP.

Mieć przeczytać tutaj: http://blog.ploeh.dk/2010/02/03/ServiceLocatorIsAnAntiPattern.aspx

I rzeczywiście jak tego faktu, że w wariancie 1, Proces wyraźniej ukazuje swoją zależność od każdej z usług Wymienia są parametry konstruktora. To sprawia, że ​​zależności są bardzo oczywiste ... i dalej myślę, że pomaga to w promowaniu dobrego projektu.

Mówiąc, że procesor bierze IContainer, nie mówi nam zbyt wiele ... i dlatego musisz przyjrzeć się bliżej, aby zidentyfikować współzależności.

+0

link jest ładny, książka wygląda dokładnie tak, jak potrzebuję. Co sądzisz o trzecim podejściu? –

+0

+1 Zobacz moją odpowiedź na dalsze komentarze. –

+1

Używam trzeciego podejścia na całym moim kodzie ... kiedy muszę wstrzyknąć środek do tworzenia wielu wystąpień typu w czasie. Używam Windsor ... więc używam delegowanych fabryk i wstrzykuję Func kiedy wiem, że mój Procesor będzie musiał stworzyć wiele IService1s z czasem ... – Jeff

0

Nie chodzi o liczbę zależności, ani o decyzję na klasę. Podejście 2 wprowadza nową zależność, ale jeśli chcesz polegać na pojemniku IoC, to jest to dobre podejście. Podejście 3 jest jak drugie, ale załóżmy trochę rzeczy w Fabryce w przyszłości. Podejście 1 jest najprostsze, nie polega na niczym i powinno być używane dla zależności, które zazwyczaj nie byłyby zarządzane przez kontener IoC.

+0

Tu nie chodzi o liczbę zależności, ani za klasę decyzją. - Czy mógłbyś rozwinąć? Powinniśmy wybrać pojedyncze podejście do całej aplikacji i używać go wszędzie lub co mówisz? Nie jestem pewien, dlaczego nie może to być decyzja na klasę. –

+0

Wybór zastosowania kontenera IoC ma wpływ na całą aplikację - każdy moduł powinien na tym polegać. Podejście 1 jest naturalne - wszyscy programiści to rozumieją. Podejście 2 jest specyficzne dla kontenera. Najgorszą rzeczą, jaką możesz zrobić, to je wymieszać. Czytelnik kodu musiałby sprawdzić, która aplikacja jest używana, gdy ma to samo miejsce dla autora testów. –

6

Odpowiedź udzielona przez JeffN825 jest poprawna, ale chciałbym dodać do tego, że nigdy nie utworzyć nową instancję procesora użyciu pojemnika takiego:

Processor proc = new Processor(
    container.Resolve<IService1>(), 
    container.Resolve<IService2>(), 
    container.Resolve<IService3>()); 

raczej Ci nie pozwolę pojemnik auto-wire zależności i resolve it one go:

Processor proc = container.Resolve<Processor>(); 
+0

dzięki, Mark. Czy mógłbyś powiedzieć coś o trzecim podejściu? Jakie są sytuacje, gdy jest to lepsze niż sugerowany procesor proc = container.Resolve (); lub 1. podejście? –

+1

Trzecie podejście to po prostu abstrakcyjna fabryka w przebraniu: http://blog.ploeh.dk/2009/05/28/DelegatesAreAnonymousInterfaces.aspx To nie jest tak naprawdę związane z dwoma innymi podejściami. Co chcesz z tym zrobić? –

+0

hej Mark, ive dodał aktualizację pytania po przeczytaniu niektórych twoich postów, co o tym sądzisz? –

Powiązane problemy