2011-08-26 12 views
8

Zgodnie z zasadami SOLID klasa nie może zależeć od innych klas, należy wstrzykiwać zależności. To proste:Inwersja zależności. Tworzenie obiektu

class Foo 
{ 
    public Foo(IBar bar) 
    { 
     this.bar = bar; 
    } 

    private IBar bar; 
} 

interface IBar 
{ 
} 

class Bar: IBar 
{ 
} 

Ale co, jeśli chcę, aby moje klasy Foo, aby móc tworzyć Bar, a nie znając dokładnej realizacji za IBAR? mogę myśleć o 4 rozwiązań tutaj, ale wszystkie z nich wydają się mieć wady:

  1. wstrzykiwanie typ obiektu i przy użyciu odbicia
  2. wykorzystujące Generics
  3. użyciu „lokalizatora usług” i nazywając determinacji () metoda.
  4. tworząc oddzieloną klasy fabrycznej i wstrzykiwanie go do Foo:

class Foo 
{ 
    public void DoSmth(IBarCreator barCreator) 
    { 
     var newBar = barCreator.CreateBar(); 
    } 
} 

interface IBarCreator 
{ 
    IBar CreateBar(); 
} 

class BarCreator : IBarCreator 
{ 
    public IBar CreateBar() 
    { 
     return new Bar(); 
    } 
} 

ostatnim przypadku wydaje się naturalne, ale klasa BarCreator ma kod zbyt litle. Jak myślisz, co jest najlepsze?

+1

Opcja 4 to poprawna odpowiedź: http://stackoverflow.com/questions/1943576/is-there-a-pattern-for-initializing-objects-created-via-a-di-container/1945023#1945023 –

+1

Jednak dlaczego chcesz, aby Foo tworzyło IBar? Bądź świadomy nieszczelnych abstrakcji. –

Odpowiedz

0

Czuję, gdy mówisz "Chcę, aby moja klasa Foo mogła tworzyć Bar", pojawia się jeszcze jedna zasada, która brzmi "Separacja obaw". Powinieneś więc delegować zadanie tworzenia klasy do czegoś innego, a Foo nie powinno się martwić o to zadanie.

2

Wszystko zależy od konkretnego scenariusza i Twoich potrzeb.
Myślę, że najczęściej stosowanym podejściem, jak już wspomniałeś, jest factory.
Jeśli używasz architektury IoC (takiej jak Ninject lub Sprint.Net, Castle Windsor itd., Patrz: here), lokalizator usług jest również dobrym rozwiązaniem.

3

Lubię "wstrzykiwać" Func<IBar> w tym przypadku. Podobnie jak:

class Foo 
{ 
    public Foo(Func<IBar> barCreator) 
    { 
     this.bar = barCreator(); 
    } 

    private IBar bar; 
} 

interface IBar 
{ 
} 
2

Jeśli masz problem z redundantnym interfejsem fabrycznym, możesz wziąć dwa podejścia tutaj.

Zrób to wielokrotnego użytku z rodzajowych:

interface IFactory<T> 
{ 
T Create(); 
} 

class DefaultConstructorFactory<T> : IFactory<T>, where T: new() 
{ 
public T Create() { return new T();} 
} 

Albo użyć funkcji anonimowej jako fabryka:

public void DoSomething(Func<IBar> barCreator) 
{ 
var newBar = barCreator(); 
//... 
} 
+0

To naprawdę nie działa, prawda? Nie można przekazać 'DefaultConstructorFactory ' jako 'IFactory ' – fearofawhackplanet

3

To właśnie fabryki zostały wykonane za.

Jeśli uważasz, że Twoja fabryka ma za mały kod, zadaj sobie pytanie, jaką korzyść daje właśnie tworzenie instancji w linii. Jeśli korzyści przewyższają koszty dodanego kodu, nie martw się o to.

Osobiście unikam serwisu serwisowego, a jeśli naprawdę musisz go użyć, to i tak chowałbym go za fabryką. Lokalizacja usługi może być łatwo nadużywana i może doprowadzić do tego, że twój kontener znajdzie się w kodzie, z którym nie powinien mieć nic wspólnego.

Dla wygody niektóre pojemniki umożliwiają określenie fabryki, która ma być używana przez kontener, gdy tworzy wystąpienie komponentu. W takim przypadku klasa może polegać bezpośrednio na IBar, ale twój kontener zadzwoni pod numer IBarCreator, kiedy będzie potrzebować nowej instancji.Castle Windsor ma na przykład metody UseFactory i UseFactoryMethod w swoim interfejsie API.