Załóżmy, że mamy 1001 klientów, którzy konstruują swoje zależności bezpośrednio, zamiast akceptować zastrzyki. Według naszego szefa refaktoryzacja 1001 nie jest opcją. W rzeczywistości nie mamy nawet dostępu do ich źródła, tylko do plików klas.
Powinniśmy „zmodernizować” system, przez który przechodzi 1001 klientów. Możemy refaktoryzować to, co lubimy. Zależności są częścią tego systemu. I niektóre z tych zależności, które mamy zmienić, aby mieć nową implementację.
Chcielibyśmy mieć możliwość konfigurowania różnych implementacji zależności, aby zaspokoić tę liczbę klientów. Niestety DI nie wydaje się opcją, ponieważ klienci nie akceptują zastrzyków z konstruktorami lub ustawiaczami.
Opcje:
1) Refaktoryzuj implementację usługi, z której korzystają klienci, aby spełniała ona potrzeby klientów. Bang, skończyliśmy. Nie elastyczny. Niezbyt skomplikowane.
2) Refaktoryzuj implementację, aby delegowała pracę do kolejnej zależności, którą nabywa za pośrednictwem fabryki. Teraz możemy kontrolować, z której implementacji korzystają wszyscy, poprzez refaktoryzację fabryki.
3) Przeprowadź refaktoryzację implementacji, aby delegowała pracę do kolejnej zależności, którą uzyskuje za pośrednictwem lokalizatora usług. Teraz możemy kontrolować, z której implementacji korzystają wszyscy, konfigurując lokalizator usług, który może być po prostu hashmap
ciągiem znaków do obiektów z niewielkim rzutowaniem.
4) Coś, o czym nawet jeszcze nie pomyślałem.
Cel:
Zminimalizuj szkody projektowe spowodowane przeciąganiem starego, źle zaprojektowanego kodu klienta w przyszłość bez zwiększania bezsensownej złożoności.
Klienci nie powinni wiedzieć ani kontrolować wdrażania swoich zależności, ale nalegają na ich budowanie new
. Nie możemy kontrolować, new
ale kontrolujemy klasę, którą budują.
Moje pytanie:
Czego nie wziąłem pod uwagę?
czy naprawdę potrzebujesz możliwości konfiguracji między różnymi implementacjami? W jakim celu?
Zwinność. Wiele niewiadomych. Kierownictwo chce możliwości zmian. Tracą jedynie zależność od świata zewnętrznego. Także testowanie.
czy potrzebujesz mechaniki czasu wykonywania, czy po prostu mechaniki kompilacji czasu, aby przełączać się między różnymi implementacjami? Dlaczego?
Prawdopodobnie wystarcza mechanika czasu kompilacji. Z wyjątkiem testów.
jakiej szczegółowości potrzebujesz, aby przełączać się między implementacjami? Wszystko na raz? Na moduł (każdy zawierający grupę klas)? Na zajęcia?
Ze 1001 tylko jeden jest uruchamiany w systemie w dowolnym momencie. Zmiana, z której korzystają wszyscy klienci naraz, jest prawdopodobnie w porządku. Prawdopodobnie ważna jest jednak indywidualna kontrola zależności.
kto musi kontrolować przełącznik? Tylko Twój zespół programistów? Administrator? Każdy klient na własną rękę? A może deweloperzy konserwacji kodu klienta? Jak łatwa / niezawodna / niezawodna musi być mechanika?
Dev do testowania. Administrator wraz ze zmianami zależności zewnętrznych urządzeń. Musi być łatwy do przetestowania i skonfigurowania.
Naszym celem jest pokazanie, że system można szybko przerobić i zmodernizować.
rzeczywisty przypadek użycia przełącznika implementacji?
Po pierwsze, niektóre dane będą dostarczane przez oprogramowanie, dopóki rozwiązanie sprzętowe nie będzie gotowe.
źródło
Odpowiedzi:
Cóż, nie jestem pewien, czy całkowicie rozumiem szczegóły techniczne i ich dokładne różnice w obsługiwanych rozwiązaniach, ale IMHO najpierw musisz dowiedzieć się, jakiego rodzaju elastyczności naprawdę potrzebujesz.
Pytania, które musisz sobie zadać to:
czy naprawdę potrzebujesz możliwości konfiguracji między różnymi implementacjami? W jakim celu?
czy potrzebujesz mechaniki czasu wykonywania, czy po prostu mechaniki kompilacji czasu, aby przełączać się między różnymi implementacjami? Dlaczego?
jakiej szczegółowości potrzebujesz, aby przełączać się między implementacjami? Wszystko na raz? Na moduł (każdy zawierający grupę klas)? Na zajęcia?
kto musi kontrolować przełącznik? Tylko Twój zespół programistów? Administrator? Każdy klient na własną rękę? A może deweloperzy konserwacji kodu klienta? Jak łatwa / niezawodna / niezawodna musi być mechanika?
Mając na uwadze odpowiedzi na te pytania, wybierz najprostsze rozwiązanie, jakie możesz wymyślić, które zapewnia wymaganą elastyczność. Nie wprowadzaj żadnej elastyczności, której nie jesteś pewien „na wszelki wypadek”, jeśli oznacza to dodatkowy wysiłek.
W odpowiedzi na twoje odpowiedzi: jeśli masz pod ręką co najmniej jeden rzeczywisty przypadek użycia, użyj go, aby zweryfikować swoje decyzje projektowe. Skorzystaj z tego przypadku użycia, aby dowiedzieć się, które rozwiązanie jest dla Ciebie najlepsze. Po prostu wypróbuj, czy „fabryka” lub „lokalizator usług” zapewnia ci to, czego potrzebujesz, lub potrzebujesz czegoś innego. Jeśli uważasz, że oba rozwiązania są równie dobre dla twojej sprawy, rzuć kostką.
źródło
Żeby upewnić się, że dobrze to rozumiem. Masz jakąś usługę złożoną z niektórych klas, powiedzmy
C1,...,Cn
i kilku klientów, którzy bezpośrednio dzwoniąnew Ci(...)
. Więc myślę, że zgadzam się z ogólną strukturą twojego rozwiązania, która polega na utworzeniu nowej usługi wewnętrznej z nowymi klasami,D1,...,Dn
które są ładne i nowoczesne i wstrzykują ich zależności (pozwalając na takie zależności przez stos), a następnie przepisuję,Ci
aby nie robić nic poza tworzeniem instancji i delgate doDi
. Pytanie brzmi, jak to zrobić, a zasugerowałeś kilka sposobów na2
i3
.Podać podobną sugestię
2
. Możesz użyć wstrzykiwania zależności w całymDi
i wewnętrznie, a następnie utworzyć normalny katalog główny kompozycjiR
(używając frameworka, jeśli uważasz to za właściwe), który jest odpowiedzialny za złożenie wykresu obiektowego. Schowaj sięR
za statyczną fabryką i pozwól każdemuCi
przejśćDi
przez to. Na przykład możesz miećZasadniczo jest to twoje rozwiązanie 2, ale gromadzi wszystkie fabryki w jednym miejscu wraz z resztą zastrzyku zależności.
źródło
getInstance()
?Zacznij od prostych, niczym wymyślnych, pojedynczych implementacji.
Jeśli później musisz utworzyć dodatkowe implementacje, jest to pytanie, kiedy nastąpi decyzja o implementacji.
Jeśli potrzebujesz elastyczności w czasie instalacji (każda instalacja klienta korzysta z jednej, statycznej implementacji), nadal nie musisz robić nic wymyślnego. Po prostu oferujesz różne biblioteki DLL lub SO (lub dowolny inny format lib). Klient musi tylko umieścić właściwy w
lib
folderze ...Jeśli potrzebujesz elastyczności środowiska wykonawczego, potrzebujesz jedynie cienkiego adaptera i mechanizmu wyboru implementacji. Niezależnie od tego, czy korzystasz z fabryki, lokalizatora czy kontenera IoC, jest to w większości dyskusyjne. Jedyne znaczące różnice między adapterem a lokalizatorem to A) Nazewnictwo i B) Czy zwracany obiekt jest singletonem czy dedykowaną instancją. A duża różnica między kontem IoC a fabryką / lokalizatorem polega na tym, kto do kogo dzwoni . (Często jest to kwestia osobistych preferencji.)
źródło