Na przykład:
var duckBehaviors = new Duckbehavior();
duckBehaviors.quackBehavior = new Quack();
duckBehaviors.flyBehavior = new FlyWithWings();
Duck mallardDuck = new Duck(DuckTypes.MallardDuck, duckBehaviors)
Ponieważ klasa Duck zawiera wszystkie zachowania (abstrakcyjne), tworzenie nowej klasy MallardDuck
(która się rozszerza Duck
) nie wydaje się być wymagane.
Odniesienie: Wzorzec projektowy Head First, rozdział 1.
design-patterns
dependency-injection
strategy-pattern
Deepak Mishra
źródło
źródło
Duckbehavior.quackBehavior
i inne pola w twoim kodzie?Odpowiedzi:
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 mafly
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 :
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.
źródło
LoginException
nie jest to dobre imię. To klasyczne „nazywanie smerfa”, a jeśli go zabioręException
, wszystko, co mamLogin
, to nie mówi mi nic o tym, co poszło nie tak.Exception
na końcu każdej klasy wyjątkowej, ale proszę nie mylić „utknąłem z nią” z „dobrym”.StackOverflow
,OutOfMemory
,NullReferenceAccess
,LoginFailure
itd. Zasadniczo, wziąć „wyjątek” od nazwy. W razie potrzeby napraw je tak, aby opisywały, co poszło nie tak.Możesz zastąpić prawie każdą metodologię inną metodologią i nadal produkować działające oprogramowanie. Jednak niektóre lepiej pasują do konkretnego problemu niż inne.
To zależy od wielu rzeczy, które są lepsze. Stan techniki w stosowaniu, doświadczenie w zespole, oczekiwany rozwój w przyszłości, osobiste preferencje i to, jak trudno będzie nowemu przybyszowi, aby się nim zająć, by wymienić tylko kilka.
Gdy zdobędziesz więcej doświadczenia i będziesz częściej zmagał się z dziełami innych ludzi, prawdopodobnie położysz większy nacisk na ostatnią kontemplację.
Dziedziczenie jest nadal ważnym i silnym narzędziem do modelowania, które niekoniecznie zawsze jest najbardziej elastyczne, ale oferuje silne wskazówki dla nowych ludzi, którzy mogą być wdzięczni za wyraźne mapowanie do dziedziny problemów.
źródło
Dziedzictwo nie jest tak ważne, jak kiedyś sądzono. Jest to nadal ważne , a usunięcie go byłoby poważnym błędem.
Skrajnym przykładem jest Objective-C, gdzie wszystko jest podklasą NSObject (kompilator faktycznie nie pozwala zadeklarować klasy, która nie ma klasy podstawowej, więc wszystko , co nie jest wbudowane w kompilator, musi być podklasą czegoś wbudowane w kompilator). Istnieje wiele przydatnych rzeczy wbudowanych w NSObject, których nie musisz przegapić.
Innym interesującym przykładem jest UIView, podstawowa klasa „view” w programowaniu UIKit na iOS. Jest to klasa, która z jednej strony przypomina trochę abstrakcyjną klasę deklarującą funkcjonalność, którą podklasy mają wypełnić, ale jest także przydatna sama w sobie. Ma podklasy dostarczane przez UIKit, które są często używane jako takie. Kompozycja programisty instaluje widoki podrzędne w widoku. Często istnieją podklasy zdefiniowane przez programistów, które często wykorzystują kompozycję. Nie ma jednej ścisłej reguły, a nawet reguł, używasz tego, co najlepiej pasuje do twoich wymagań.
źródło
type
, każda dziedzina, która nie jest prymitywna w Javie, dziedziczyObject
po prototypieObject
itd.[Zaryzykuję bezczelną odpowiedź na tytułowe pytanie.]
Tak ... z wyjątkiem tego, że sam wzorzec strategii wykorzystuje dziedziczenie. Wzorzec strategii działa na dziedziczeniu interfejsu. Zastąpienie dziedziczenia kompozycją + strategią przenosi dziedziczenie w inne miejsce. Niemniej jednak takie zastąpienie często jest warte wykonania, ponieważ pozwala na podrażnianie hierarchii .
źródło
interface
projekt craptastic z dziedziczeniem. Ukrytym problemem tutaj jest niespójna implementacja zachowań zależnych od morfingu. P, S. dziedziczenie i interfejs nie wykluczają się wzajemnie,interface
Nie. Jeśli kaczka krzyżówka wymaga innych parametrów do wystąpienia niż jakikolwiek inny rodzaj kaczki, zmiana koszmaru byłaby koszmarem. Czy powinieneś być również w stanie utworzyć kaczkę? To bardziej kaczka krzyżówka, kaczka mandarynka lub jakikolwiek inny rodzaj kaczki.
Mogę też potrzebować trochę logiki wokół typów, aby hierarchia typów mogła być lepsza.
Teraz, jeśli ponowne użycie kodu staje się problematyczne dla niektórych funkcji, możemy je skomponować, przekazując funkcje przez konstruktor.
Ponownie, to naprawdę zależy od twojego przypadku użycia. Jeśli masz tylko kaczkę i kaczkę krzyżówkę, hierarchia klas jest znacznie prostszym rozwiązaniem.
Ale oto przykład, w którym chcesz zastosować wzorzec strategii: jeśli masz klasy klientów i chcesz przekazać strategię rozliczeniową (interfejs), która może być strategią rozliczeń Happy Hour lub zwykłą strategią rozliczeń, możesz ją przekazać w konstruktorze. W ten sposób nie musisz tworzyć dwóch różnych klas klientów i nie potrzebujesz hierarchii. Masz tylko jedną klasę klienta.
źródło