Jasne, ale nazywamy ten skład i delegację . Wzorzec strategii i wstrzykiwanie zależności może wydawać się strukturalnie podobne, ale ich zamiary są różne.
Wzorzec strategii pozwala modyfikować zachowanie w czasie wykonywania w ramach tego samego interfejsu. Mógłbym polecić kaczce krzyżówki latać i patrzeć, jak leci skrzydłami. Następnie zamień go na kaczkę odrzutowca i obserwuj, jak leci liniami Delta. Wykonanie tego podczas działania programu jest kwestią Wzorca Strategii.
Wstrzykiwanie zależności jest techniką pozwalającą uniknąć zależności kodowania na sztywno, dzięki czemu mogą one zmieniać się niezależnie, bez konieczności modyfikacji klientów po ich zmianie. Klienci po prostu wyrażają swoje potrzeby, nie wiedząc, w jaki sposób zostaną zaspokojone. Tak więc sposób ich spełnienia jest ustalany gdzie indziej (zazwyczaj głównie). Nie potrzebujesz dwóch kaczek, aby skorzystać z tej techniki. Po prostu coś, co korzysta z kaczki, nie wiedząc ani nie dbając o nią. Coś, co nie buduje kaczki lub nie szuka jej, ale jest całkowicie zadowolone z użycia dowolnej kaczki, którą ci podajesz.
Jeśli mam konkretną klasę kaczki, mogę ją wprowadzić w życie. Mógłbym nawet zmienić zachowanie z latania ze skrzydłami na latanie z deltą w oparciu o zmienną stanu. Że zmienna może być logiczna, int, lub może być FlyBehavior
, że ma fly
metodę, która robi co mi latający styl bez konieczności przetestowania go if. Teraz mogę zmieniać style latania bez zmiany typów kaczek. Teraz Krzyżówki mogą zostać pilotami. To jest skład i delegacja . Kaczka składa się z FlyBehavior i może przekazywać jej prośby o latanie. Możesz w ten sposób zastąpić wszystkie zachowania kaczki na raz lub zatrzymać coś dla każdego zachowania lub dowolnej kombinacji pomiędzy.
To daje ci te same moce, które ma dziedzictwo, z wyjątkiem jednej. Dziedziczenie pozwala wyrazić metody kaczki, które zastępujesz w podtypach kaczki. Skład i delegowanie wymaga od Kaczki jawnego delegowania do podtypów od samego początku. Jest to o wiele bardziej elastyczne, ale wymaga więcej pisania na klawiaturze i Duck musi wiedzieć, że to się dzieje.
Jednak wiele osób uważa, że od samego początku należy wyraźnie przewidzieć dziedziczenie. A jeśli tak nie było, należy oznaczyć swoje klasy jako zapieczętowane / końcowe, aby nie dopuścić do dziedziczenia. Jeśli weźmiesz ten pogląd, dziedziczenie naprawdę nie ma przewagi nad kompozycją i delegowaniem. Ponieważ w obu przypadkach musisz albo zaprojektować rozszerzalność od samego początku, albo być gotowym na później.
Burzenie rzeczy jest w rzeczywistości popularną opcją. Pamiętaj tylko, że w niektórych przypadkach jest to problem. Jeśli niezależnie wdrożyłeś biblioteki lub moduły kodu, których nie zamierzasz aktualizować w następnej wersji, możesz skończyć z wersjami klas, które nie wiedzą nic o tym, co robisz.
Chociaż chęć późniejszego zniszczenia może uwolnić cię od nadmiernego projektowania, jest coś bardzo potężnego w tym, że możesz zaprojektować coś, co używa kaczki, bez konieczności dowiedzenia się, co kaczka faktycznie zrobi, gdy zostanie użyta. To niewiedza jest potężną rzeczą. Pozwala ci na chwilę przestać myśleć o kaczkach i pomyśleć o reszcie kodu.
„Czy możemy” i „powinniśmy” to różne pytania. Preferuj kompozycję zamiast dziedziczenia nie oznacza, że nigdy nie używaj dziedziczenia. Nadal istnieją przypadki, w których dziedziczenie jest najbardziej sensowne. Pokażę ci mój ulubiony przykład :
public class LoginFailure : System.ApplicationException {}
Dziedziczenie pozwala tworzyć wyjątki z bardziej szczegółowymi, opisowymi nazwami w jednym wierszu.
Spróbuj zrobić to z kompozycją, a dostaniesz bałagan. Ponadto nie ma ryzyka wystąpienia problemu jo-jo, ponieważ nie ma tu danych ani metod umożliwiających ponowne użycie i zachęcanie do tworzenia łańcucha spadkowego. Wszystko to dodaje dobre imię. Nigdy nie lekceważ wartości dobrego imienia.
Duckbehavior.quackBehavior
i inne pola w twoim kodzie?