Dlaczego nie użyć kontenera IoC do rozwiązywania zależności między jednostkami / obiektami biznesowymi?

82

Rozumiem koncepcję DI, ale dopiero uczę się, co mogą zrobić różne kontenery IoC. Wygląda na to, że większość ludzi opowiada się za używaniem kontenerów IoC do łączenia usług bezstanowych, ale co z używaniem ich do obiektów stanowych, takich jak encje?

Niezależnie od tego, czy jest to dobre, czy złe, zwykle napycham swoje byty zachowaniem, nawet jeśli to zachowanie wymaga zewnętrznej klasy. Przykład:

public class Order : IOrder
{

    private string _ShipAddress;
    private IShipQuoter _ShipQuoter;

    public Order(IOrderData OrderData, IShipQuoter ShipQuoter)
    {
        // OrderData comes from a repository and has the data needed 
        // to construct order
        _ShipAddress = OrderData.ShipAddress;  // etc.
        _ShipQuoter = ShipQuoter;

    }

    private decimal GetShippingRate()
    {
        return _ShipQuoter.GetRate(this);
    }
}

Jak widać, zależności są wstrzykiwane przez konstruktor. A teraz kilka pytań.

  1. Czy uzależnienie podmiotów od zewnętrznych klas, takich jak ShipQuoter, jest uważane za złą praktykę? Wydaje się, że wyeliminowanie tych zależności prowadzi mnie do domeny anemicznej, jeśli dobrze rozumiem definicję.

  2. Czy używanie kontenera IoC do rozwiązywania tych zależności i konstruowania jednostki w razie potrzeby jest złą praktyką? Czy da się to zrobić?

Dzięki za wgląd.

Casey Wilkins
źródło
3
po prostu rób rzeczy, które musisz, ponieważ ułatwia to pracę, a nie dlatego, że prawdopodobnie tak powinieneś to robić
Omu
28
Jako programista-samouk poszedłem tą drogą i doprowadziło to do oprogramowania, które jest obecnie używane przez moją firmę. Pojawiło się oprogramowanie oparte na skryptach proceduralnych / transakcyjnych, częściowo dlatego, że było najłatwiejsze i zabawne, ponieważ nie znałem nic lepszego. Utrzymywanie i rozwijanie jest absolutnie bolesne, dlatego poświęcam czas na ponowne napisanie go i szukam rady od ludzi, którzy już przezwyciężyli te problemy, jak nie popełniać tych samych błędów.
Casey Wilkins
1
related: stackoverflow.com/questions/827670/…
Ruben Bartelink

Odpowiedzi:

90

Najtrudniej odpowiedzieć na pierwsze pytanie. Czy to zła praktyka, gdy jednostki są zależne od zewnętrznych klas? Z pewnością nie jest to najczęstsza rzecz.

Jeśli, na przykład, wstrzykniesz Repozytorium do swoich Encji, masz efektywną implementację wzorca Active Record . Niektórzy ludzie lubią ten wzór ze względu na wygodę, którą zapewnia, podczas gdy inni (jak ja) uważają go za zapach kodu lub anty-wzór, ponieważ narusza on zasadę pojedynczej odpowiedzialności (SRP).

Możesz argumentować, że wstrzyknięcie innych zależności do jednostek pociągnie cię w tym samym kierunku (z dala od SRP). Z drugiej strony z pewnością masz rację, że jeśli tego nie zrobisz, pociągnie się w kierunku modelu domeny anemicznej .

Walczyłem z tym wszystkim przez długi czas, dopóki nie natknąłem się na ( porzucony ) artykuł Grega Younga na temat DDDD, w którym wyjaśnia, dlaczego stereotypowa architektura n-warstwowa / n-warstwowa zawsze będzie CRUDy (a zatem raczej anemiczna).

Przeniesienie naszej uwagi na modelowanie obiektów domeny jako poleceń i zdarzeń zamiast rzeczowników wydaje się umożliwiać nam zbudowanie odpowiedniego, zorientowanego obiektowo modelu domeny.

Na drugie pytanie łatwiej odpowiedzieć. Zawsze możesz użyć Abstract Factory do tworzenia instancji w czasie wykonywania . Z Castle Windsor możesz nawet skorzystać z Fabryki Typów, zwalniając cię z ciężaru ręcznego wdrażania fabryk.

Mark Seemann
źródło
Dzięki Mark. Widziałem Fabrykę Typów i przeczytałem twoje inne posty na temat metody Fabryki Abstrakcyjnej, ale nigdy nie widziałem żadnych przykładów ich użycia do rozwiązywania encji. Czy dzieje się tak, ponieważ większość ludzi projektuje swoje jednostki bez żadnych zależności poza repozytorium? Czy będę mieć kłopoty w przyszłości, jeśli rygorystycznie wykorzystam coś takiego jak Fabryka Typed, aby rozwiązać moje jednostki, które mają zewnętrzne zależności?
Casey Wilkins
Chodziło o to, że jeśli Twoje jednostki zawierają innych współpracowników, którzy mogą mieć dostęp do innych jednostek, itp., Możesz napotkać różnego rodzaju problemy z konserwacją - nie wspominając o naruszeniach SRP i problemach N + 1. Dlatego Evans zaleca traktowanie każdego bytu jako zagregowanego źródła.
Mark Seemann
W moim przykładzie ShipQuoter pobiera stawki wysyłki dla zamówienia z usługi internetowej (np. UPS). Czy uczyniłbyś to usługą, która akceptuje IOrder, czy uczyniłbyś ją częścią obiektu domeny, takiego jak Order.GetRates?
Casey Wilkins
Spędziłbym dużo czasu zastanawiając się, jak w pierwszej kolejności uniknąć synchronicznego przyciągania. Im bardziej pobierasz dane w synchroniczny, blokujący sposób, tym bardziej kruchy staje się twój projekt. Dlatego CQRS jest tak atrakcyjną alternatywą.
Mark Seemann
4
Twój link do gazety Grega jest martwy. Ale nadal jest dostępny tutaj . Wygląda na to, że jest to nowsza wersja.
BornToCode,
1

Wiem, że to stary post, ale chciałem dodać. Jednostka domeny nie powinna zachowywać się sama, nawet jeśli przekazujesz abstrakcyjne repozytorium w ctor. Sugeruję, że to nie tylko narusza SRP, ale także jest sprzeczny z agregacją DDD. Pozwólcie, że wyjaśnię, DDD nadaje się do złożonych aplikacji z nieodłącznie głębokimi wykresami, dlatego używamy korzeni zbiorczych lub kompozytowych, aby utrwalić zmiany w podstawowych „dzieciach”, więc kiedy wstrzykujemy wytrwałość poszczególnym dzieciom, naruszamy ich relacje z złożony lub zagregowany rdzeń, który powinien „odpowiadać” za cykl życia lub agregację. Oczywiście złożony pierwiastek lub agregat również nie zachowuje swojego własnego wykresu. Innym jest to, że wstrzykiwanie zależności obiektów DDD polega na tym, że wstrzyknięty obiekt domeny faktycznie nie ma stanu, dopóki nie nastąpi inne zdarzenie w celu uwodnienia jego stanu. Każdy konsument kodu będzie zmuszony najpierw zainicjować lub skonfigurować obiekt domeny, zanim będzie mógł wywołać zachowanie biznesowe, które narusza hermetyzację.

user1538467
źródło