Uczę się tylko o wstrzykiwaniu zależności i utknąłem na czymś. Dependency Injection zaleca wysyłanie klas zależnych przez konstruktor, ale zastanawiam się, czy jest to konieczne w przypadku obiektów danych. Ponieważ testowanie jednostkowe jest jedną z głównych zalet DI, czy obiekt danych, który przechowuje tylko dane, a nie jakiekolwiek procedury, kiedykolwiek byłby testowany jednostkowo, co czyni DI niepotrzebną warstwą złożoności, czy też pomaga w pokazywaniu zależności nawet z obiektami danych?
Class DO{
DO(){
DataObject2List = new List<DO2>();
}
public string Field1;
public string Field2;
public List<DO2> DataObject2List;
}
Class DO2{
public DateTime Date;
public double Value;
}
c#
dependency-injection
class-design
sooprise
źródło
źródło
Odpowiedzi:
Chciałbym zasugerować wyjaśnienie niektórych terminów, których tu używasz, w szczególności „zależności” i „wstrzykiwania zależności”.
Zależność:
„Zależność” jest zazwyczaj złożonym obiektem, który wykonuje pewne funkcje, od których może zależeć inna klasa. Klasycznymi przykładami mogą być rejestrator lub moduł dostępu do bazy danych lub jakiś komponent przetwarzający określoną logikę biznesową.
Obiekt zawierający tylko dane, taki jak DTO lub obiekt wartości, nie jest zwykle określany jako „zależność”, ponieważ nie pełni on niektórych potrzebnych funkcji.
Gdy spojrzeć na to w ten sposób, co robisz w swoim przykładzie ( komponując ten
DO
obiekt z listyD02
obiektów przez konstruktora) nie powinny być uważane za „zastrzyk dependecy” w ogóle. To tylko ustawienie nieruchomości. Od Ciebie zależy, czy podasz je w konstruktorze, czy w inny sposób, ale przekazanie go przez konstruktor nie spowoduje wstrzyknięcia zależności.Wstrzykiwanie zależności:
Gdyby twoja
DO2
klasa faktycznie zapewniała dodatkowe funkcje, którychDO
potrzebuje klasa, to naprawdę byłaby to zależność. W takim przypadku klasa zależnaDO
powinna zależeć od interfejsu (takiego jak ILogger lub IDataAccessor), a z kolei polegać na kodzie wywołującym, który zapewnia ten interfejs (innymi słowy, „wstrzykuje” go doDO
instancji).Wstrzyknięcie zależności w taki sposób sprawia, że
DO
obiekt jest bardziej elastyczny, ponieważ każdy inny kontekst może zapewnić własną implementację interfejsu doDO
obiektu. (Pomyśl o testowaniu jednostkowym.)źródło
Zrobię co w mojej mocy, aby przezwyciężyć zamieszanie w pytaniu.
Przede wszystkim „obiekt danych” nie jest znaczącym terminem. Jeśli jedyną cechą charakterystyczną tego obiektu jest to, że nie ma on metod, to w ogóle nie powinien istnieć . Przydatny obiekt bez zachowania powinien pasować do co najmniej jednej z następujących podkategorii:
Obiekty wartości lub „rekordy” nie mają żadnej tożsamości. Powinny być typami wartości , z semantyką kopiowania na podstawie odniesienia, zakładając, że środowisko to obsługuje. Ponieważ są to stałe struktury, VO powinien być tylko typem pierwotnym lub ustaloną sekwencją elementów pierwotnych. Dlatego VO nie powinien mieć żadnych zależności ani powiązań; każdy inny niż domyślny konstruktor istniałby wyłącznie w celu zainicjowania wartości, tzn. ponieważ nie można jej wyrazić dosłownie.
Obiekty przesyłania danych są często mylnie mylone z obiektami wartości. DTOs zrobić mieć tożsamość, lub przynajmniej może . Jedynym celem DTO jest ułatwienie przepływu informacji z jednej domeny do drugiej. Nigdy nie mają „zależności”. Oni mogą mieć skojarzenia (czyli do tablicy lub kolekcji), ale większość ludzi woli, aby ich mieszkania. Zasadniczo są one analogiczne do wierszy danych wyjściowych zapytania do bazy danych; są to obiekty przejściowe, które zwykle wymagają utrwalenia lub serializacji i dlatego nie mogą odwoływać się do żadnych abstrakcyjnych typów, ponieważ uniemożliwiłoby to ich użycie.
Wreszcie obiekty dostępu do danych zapewniają opakowanie lub fasadę do bazy danych pewnego rodzaju. Te oczywiście mają zależności - zależą od połączenia z bazą danych i / lub składników trwałości. Jednak ich zależności są prawie zawsze zarządzane zewnętrznie i całkowicie niewidoczne dla dzwoniących. We wzorze Active Record jest to struktura, która zarządza wszystkim poprzez konfigurację; w starszych (dawnych jak na dzisiejsze standardy) modelach DAO można było je kiedykolwiek zbudować tylko za pomocą kontenera. Gdybym widział jeden z nich z zastrzykiem konstruktora, byłbym bardzo, bardzo zmartwiony.
Możesz być także myślenie o obiekcie podmiotu lub „obiektu biznesowego” , aw tym przypadku nie chcą wstrzykiwania zależności wsparcie, ale nie w taki sposób, że uważasz lub z powodów myślisz. Nie służy to kodowi użytkownika , lecz menedżerowi jednostki lub ORM, który po cichu wstrzykuje serwer proxy, który przechwytuje, aby robić wymyślne rzeczy, takie jak rozumienie zapytań lub leniwe ładowanie.
W takich przypadkach zwykle nie udostępnia się konstruktora do wstrzykiwania; zamiast tego wystarczy uczynić właściwość wirtualną i użyć typu abstrakcyjnego (np.
IList<T>
zamiastList<T>
). Reszta dzieje się za kulisami i nikt nie jest mądrzejszy.Podsumowując, powiedziałbym, że widoczny wzorzec DI zastosowany do „obiektu danych” jest niepotrzebny i prawdopodobnie nawet czerwona flaga; ale w dużej mierze wynika to z faktu, że samo istnienie obiektu jest czerwoną flagą, z wyjątkiem przypadku, gdy jest on konkretnie używany do reprezentowania danych z bazy danych. W prawie każdym innym przypadku jest to zapach kodu, zazwyczaj początki Anemicznego Modelu Domeny lub przynajmniej Poltergeist .
Powtarzać:
Płetwa.
źródło
W twoim przykładzie
DO
nie ma żadnych zależności funkcjonalnych (zasadniczo dlatego, że nic nie robi). Jest zależny od konkretnego typuDO2
, więc możesz chcieć wprowadzić interfejs do abstraktuDO2
, aby konsument mógł zaimplementować własną konkretną implementację klasy potomnej.Naprawdę, jaką zależność tu zastrzygłbyś?
źródło
DOPersister
która wie, jak sięDO
zachować, i pozostawić ją jako obiekt wyłącznie danych (moim zdaniem lepiej). W tym drugim przypadkuDOPersister
zostanie wstrzyknięta zależność od bazy danych.Ponieważ jest to obiekt danych w warstwie dostępu do danych, powinien zależeć bezpośrednio od usługi bazy danych. Możesz podać Konstruktor bazy danych:
Ale zastrzyk nie musi być w konstruktorze. Alternatywnie można podać zależność za pomocą każdej metody CRUD. Wolę tę metodę od poprzedniej, ponieważ Twój obiekt danych nie musi wiedzieć, gdzie będzie trwał, dopóki nie będziesz musiał go utrwalić.
Na pewno nie chcesz ukryć konstrukcji w metodach CRUD!
Alternatywną opcją byłoby zbudowanie usługi DatabaseService za pomocą metody klasy nadpisywalnej.
Ostatnią alternatywą jest użycie ServiceLocator w stylu singleton. Chociaż nie podoba mi się ta opcja, można ją testować jednostkowo.
źródło