8

czytałem tutaj różne inne pytanie na argumencie, przedeznaleźć właściwy korzeń kompozycji do biblioteki .NET

Dependency Inject (DI) “friendly” library

Ioc/DI - Why do I have to reference all layers/assemblies in entry application?

article i ten (i inne różne materiały) .

Jednak nie jest dla mnie jasne, gdzie umieścić kompozycję root w projekcie biblioteki .NET. Projekt nie należy do żadnego konkretnego typu wymienionego w tym artykule. W przypadku komputerów stacjonarnych, konsoli, a nawet aplikacji internetowych ten punkt jest wyraźnie zdefiniowany.

Moje obecne podejście do zawijania pojemnika, zarejestruj rodzajów i ponownie wystawiać metodę Resolve:

class DefaultBootstrapper : IBootstrapper { 
    public Bootstrapper() { 
    _container = new XXXContainer(); 
    RegisterTypes(_container); 
    } 

    public T Resolve<T>() where T : class { 
    return _container.Resolve<T>(); 
    } 

    // + other _container.Resolve() overloads 

    private readonly XXXContainer _container; 
} 

Potem zapobiec konsumentów biblioteki do tworzenia instancji administratora biblioteki (np określającą konstruktorów wewnętrznych) a tym samym wymuszając stosowanie fabryce jednoelementowy:

class XYZFactory { 
    static XYZFactory() {} 

    private XYZFactory(IBootstrapper bootstrapper) { 
    _bootstrapper = bootstrapper; 
    } 

    public static XYZFactory Instance { 
    get { return Singleton; } 
    } 

    public ABCType CreateABCType(string param1) { 
    return _bootstrapper.Resolve<ABCType>(param1, _bootstrapper.Resolve<Dependency1>); 
    } 

    private static readonly XYZFactory Singleton = XYZFactory(new DefaultBootstrapper); 
    private readonly IBootstrapper _bootstrapper; 
} 

pytanie brzmi, jest to lepsze podejście lub lepszy wzór do zatrudniania dla zlokalizowania compositi na root w projekcie bibliotecznym?

Odpowiedz

13

To zależy od typu biblioteki, którą tworzysz. Czy twój projekt biblioteczny jest częścią twojego własnego rozwiązania, czy też jest biblioteką do wielokrotnego użytku, której inni deweloperzy zależą od Twojego zespołu, działu, a nawet organizacji?

W przypadku, gdy jest to tylko część projektu rozwiązania bibliotecznego, projekt biblioteczny nie powinien zwykle zawierać katalogu głównego kompozycji. Z definicji composition root jest "(najlepiej) unikalną lokalizacją w aplikacji, w której moduły są złożone razem". Innymi słowy, twoje rozwiązanie będzie miało jeden lub wiele projektów start-up (takich jak aplikacja MVC, usługa WCF, aplikacja konsolowa), a każdy projekt startowy otrzyma własny katalog główny kompozycji. Poniższe warstwy nie uzyskałyby własnej kompozycji głównej.

To nie oznacza, że ​​nie należy zapobiegać duplikacji kodu wewnątrz pierwiastków kompozycji. W przypadku powielenia spowodowanego przez domyślne okablowanie dołączonych projektów (takich jak DAL i BLL), zazwyczaj należy wyodrębnić tę logikę do innego projektu. Możesz to zrobić, włączając część logiki rejestracyjnej do jednego z projektów (najprawdopodobniej BLL) i pozwalając każdemu składowi root wywołać tę wspólną logikę, lub możesz to zrobić, dodając specjalny projekt "bootstrapper" dla tego projektu i projekty referencyjne. Ten projekt bootstrapera będzie zawierał tylko logikę rejestracji. Oddzielając tę ​​logikę od złożeń aplikacji, zapobiegasz, aby te złożenia wymagały zależności od używanej biblioteki wstrzyknięć zależności. Zazwyczaj nie stanowi to problemu, jeśli zestaw pobiera zależność od takiej biblioteki, o ile upewnia się, że logika aplikacji nie pobiera zależności od kontenera.

Dla bibliotek wielokrotnego użytku rzeczy zazwyczaj są różne. W takim przypadku konsumenci będą korzystać z Twojej biblioteki, ale nie masz kontroli nad tym, jak układają swoją aplikację. Często chcesz dostarczać bibliotekę w sposób, który może być bezpośrednio konsumowany przez konsumentów, bez konieczności wykonywania "złożonej" rejestracji w głównym katalogu. Często nawet nie wiesz, czy w ogóle mają korzeń kompozycji.

W takim przypadku zazwyczaj należy sprawić, aby biblioteka działała bez pojemnika DI.Nie powinieneś polegać na takim kontenerze, ponieważ mogłoby to przeciągnąć pojemnik. Jeśli używasz pojemnika, zapytaj siebie, dlaczego twoja biblioteka wielokrotnego użytku używa kontenera, a jeśli tak być musi. Być może robisz to dlatego, że zaprojektowałeś wszystkie typy w oparciu o zasadę wtrysku zależności; ponieważ to ułatwia testowanie. Nie zapominaj, że to jest twój problem, a nie problem Twoich konsumentów. Jako projektant bibliotek wielokrotnego użytku powinieneś bardzo starać się, aby Twoja biblioteka była jak najbardziej użyteczna dla Twoich klientów. Nigdy nie zakładaj, że twoi konsumenci używają pojemnika DI. Nawet jeśli będą ćwiczyć Inwencję Dependowania, mogą zastosować raczej Pure DI niż Pojemnik DI.

Jeśli budujesz bibliotekę wielokrotnego użytku, spójrz na numer this blog od Marka Seemanna.

+0

Przykro mi, nie mogę jeszcze głosować (ale będę na 15+). Kiedy interesowałem się właśnie ** scenariuszem ramowym **, kiedy kontener (jeśli jest obecny) ukryłby się przed _public interface_, przedstawiłeś mi wiele różnych. Czy mogę prosić o odniesienie do pogłębienia argumentu (artykuł lub książka na blogu) o iniekcji __poor-man__, które w przypadku bibliotek wielokrotnego użytku stały się przydatne? Jeszcze raz dziękuję – jay

+1

Nie mogę odwołać się do żadnej konkretnej pracy, aby utworzyć kopię moich argumentów, ale ogólnie książka Marka Seemanna "Dependency Injection w .NET" jest najlepszym odniesieniem dla DI, chociaż głównie mówi o scenariuszu LOB. Możesz spojrzeć na kod źródłowy Microsoft Enterprise Library. Zwróć uwagę na E.L. bierze ciężką zależność od Unity i jest czymś, o czym myślę, że powinieneś próbować zapobiec, pisząc bibliotekę wielokrotnego użytku. – Steven

+1

To brzmi jak potwierdzenie, że _Dep. Wstrzykiwanie .NET_ jest dla mnie wymaganą lekturą. Pogłębię się dalej w tym sporze, ale, powracając do wielokrotnego użytku, pytanie o bibliotekę, myślę, że ** wtórny "zastrzyk złego człowieka" w tym przypadku (który, jak pan twierdzi, jest wyjątkowy), jest właściwym wyborem. Umożliwia testowanie jednostkowe (poprzez atrybut ** InternalsVisibleTo **) i jest to dla mnie centralne wymaganie. – jay