Pytanie o wzór dekoratora i klasy abstrakcyjnego dekoratora? [duplikat]

To pytanie ma już odpowiedź tutaj:

To pytanie zostało już zadane tutaj , ale zamiast odpowiadać na konkretne pytanie, podano opisy działania wzoru dekoratora. Chciałbym zapytać jeszcze raz, ponieważ odpowiedź nie jest to dla mnie od razu oczywiste, po prostu czytając, jak działa wzór dekoratora (czytałem artykuł w Wikipedii i sekcję w książce Head First wzorce projektowe).

Zasadniczo chcę wiedzieć, dlaczego musi zostać utworzona abstrakcyjna klasa dekoratora, która implementuje (lub rozszerza) jakiś interfejs (lub klasę abstrakcyjną). Dlaczego wszystkie nowe "klasy dekorowane" nie mogą po prostu zaimplementować (lub rozszerzyć) podstawowego abstrakcyjnego obiektu (zamiast rozszerzać abstrakcyjny dekorator klasy)?

Aby uczynić to bardziej konkretnym, użyję przykładu z książki wzorców projektowych dotyczącej napojów kawowych:

  • istnieje abstrakcyjna klasa komponentów o nazwie Beverage
  • proste typy napojów, takie jak HouseBlend po prostu rozszerz Beverage
  • do dekoracji napoju, tworzona jest klasa abstrakcyjna CondimentDecorator, która rozszerza Beverage i ma instancję Beverage
  • powiedzmy, że chcemy dodać przyprawę "mleczną", tworzy się klasa Milk, która rozszerza CondimentDecorator

Chciałbym zrozumieć, dlaczego potrzebowaliśmy klasy CondimentDecorator i dlaczego Klasa Milk nie mogła po prostu rozszerzyć samej klasy Beverage i przekazać instancję Beverage w jej konstruktorze.

Mam nadzieję, że to jasne... jeśli nie chciałbym po prostu wiedzieć dlaczego Klasa abstrakcyjnego dekoratora jest niezbędna dla tego wzoru ? Dzięki.

Edit: próbowałem to zaimplementować, pomijając klasę abstrakcyjnego dekoratora, a to wydaje się nadal praca. Czy ta abstrakcyjna klasa jest obecna we wszystkich opisach tego wzoru tylko dlatego, że zapewnia standardowy interfejs dla wszystkich nowych klas zdobionych?

Author: Community, 2010-01-07

9 answers

Umożliwia zdobienie klasy bazowej niezależnie za pomocą różnych dekoratorów w różnych kombinacjach bez konieczności wyprowadzania klasy dla każdej możliwej kombinacji. Na przykład, powiedzmy, że chcesz swój Beverage z mlekiem i gałką muszkatołową. Używając dekoratorów bazujących na abstrakcyjnej klasie Dekoratorów, po prostu owijasz za pomocą dekoratorów Milk i Nutmeg. Jeśli pochodzi z Beverage, musiałbyś mieć klasę MilkWithNutmegBeverage i klasę a MilkBeverage i klasę A NutmegBeverage. Można sobie wyobrazić, jak to wybucha jako zwiększa się liczba możliwych kombinacji. Użycie abstrakcyjnej implementacji dekoratora zmniejsza to do TYLKO JEDNEJ implementacji klasy dekoratora na dekorację.

 9
Author: tvanfosson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-01-06 22:29:52

Lepiej półtora roku późno niż wcale:

Klasa bazowa dla dekoratorów określonego interfejsu nie jest konieczna, ale jest bardzo przydatna.

Jest to przydatne, z jednej strony jako sposób dokumentowania, że klasy wywodzące się z niego są dekoratorami danego interfejsu, ale głównie dlatego, że dekoratorzy zwykle nie muszą dodawać funkcjonalności do każdej metody dekoratora interfejsu, więc podstawowa klasa dekoratora pozwala dekoratorom pochodnym zaimplementować tylko te metody interfejsu, do których faktycznie muszą dodać jakąś funkcjonalność, pozostawiając resztę metod klasie bazowej, aby zapewnić domyślną implementację dla. (Które po prostu deleguje wezwanie do decoree.)

Kontrast to z pisaniem dekoratorów, które implementują zdobiony interfejs od podstaw, gdzie kompilator wymaga, aby dostarczyć implementację dla każdej metody interfejsu, czy dekorator będzie dodawanie żadnej funkcjonalności do niego, albo i nie.

To takie proste, naprawdę.

 25
Author: Mike Nakis,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-12-02 16:23:55

Zastanawiałam się nad tym samym. Wracając do źródła, wzorce projektowe GOF, widzę to pod "implementacją" w rozdziale dekorator:

" pomijając klasę abstrakcyjnego dekoratora. Nie ma potrzeby definiowania abstrakcyjnej klasy dekoratora, gdy wystarczy dodać tylko jedną odpowiedzialność. Często tak jest, gdy masz do czynienia z istniejącą hierarchią klas, a nie projektowaniem nowej. W takim przypadku można scalić odpowiedzialność dekoratora za przekazywanie żądań do komponent do dekoratora betonu."

Więc przynajmniej w takim razie wydaje się, że GOF się z Tobą zgadza: -)

Nie jestem pewien, co oznacza "jedna odpowiedzialność". Nie jestem pewien, czy więcej niż "jedna odpowiedzialność" oznaczałaby jednego dekoratora betonu, który ma więcej niż jedną odpowiedzialność, czy więcej niż jednego dekoratora betonu, z których każdy ma jedną odpowiedzialność. Tak czy inaczej, nie rozumiem, dlaczego abstrakcyjny dekorator jest konieczny. Moim zdaniem odpowiedź tvanfossona (w komentarzu do jego odpowiedź własna) jest właściwa - że po rozpoczęciu tworzenia kilku klas dekorowania, wyjaśnia decyzję projektową, aby grupować je w superklasę. Z drugiej strony, gdzie jest tylko jedna klasa, być może sprawia, że decyzja o projekcie jest mniej jasna, jeśli dodasz drugą klasę, która po prostu siedzi jako bezsensowny pośrednik między komponentem bazowym a dekoratorem (powiedziawszy to, jest dość prawdopodobne, że będziesz chciał dodać więcej w pewnym momencie, więc może lepiej włączyć abstrakcyjny dekorator nawet w jednym przypadku...)

W każdym razie, wydaje się, że ma to związek z wyjaśnieniem projektu, a nie z różnicą między kodem działającym, a nie.

 10
Author: Jeremy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-06-08 01:03:44

(Trochę za późno na twoje pytanie..)

Spędziłem też sporo czasu, próbując znaleźć odpowiedź. W moim przypadku dekorator non-concrete rozszerza klasę do dekoracji ("decoree"), zamiast interfejsu wspólnego zarówno dla dekoratora, jak i dekoratora.

Po przeczytaniu z różnych źródeł, wydaje mi się, że poza tym, co powiedział tvanfosson , powodem, aby konkretni dekoratorzy rozszerzyli abstrakcyjny lub bardziej ogólny dekorator, jest to, że nie powtarzamy tego kod "delegacja" w kółko.

[Beverage]<———————[(abstract) CondimentDecorator]<—————[Milk]
[decoree ]——————<>[ adds code to efficiently    ]
                  [ forward calls to decorated  ]<—————[LemonJuice]
                  [ instance of Beverage        ]

W Twoim przypadku, twój abstrakcyjny dekorator rozszerzy decoree i zaimplementuje ten sam interfejs co decoree, delegując / przekazując do niego wszystkie wywołania metod. Następnie betonowe dekoratory, które możesz zbudować, będą musiały tylko wdrożyć te metody, w których chcesz zrobić coś innego niż tylko przekazać wywołanie metody do decoree.

Mam nadzieję, że wyraziłem się jasno.. :- S Osobiście jestem trochę rozczarowany konieczność powtórzenia interfejsu decoree w dekoratorze. Dodaje to pewne sprzężenie, ponieważ za każdym razem zmiany interfejsu decoree (jak coraz więcej metod), dekorator musi nadrobić zaległości.

W PHP 5.3.2 (tak, Wiem, że twoje pytanie jest związane z Javą), powinno być jednak możliwe dynamiczne wychwytywanie wszystkich wywołań metod i przesyłanie ich wszystkich, bez potrzeby dekoratora, aby wiedzieć, które metody są wywoływane (w niezbyt efektywny sposób, jednak, aby użyć odbicia API). Myślę, że jest to możliwe w Ruby i innych językach już.

PS: To moja pierwsza odpowiedź w SO! ^_^
 10
Author: Rafa,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:26:14

Dekorator bazowy ułatwia tworzenie dodatkowych dekoratorów. Wyobraź sobie, że napój ma dziesiątki abstrakcyjnych metod lub jest interfejsem, powiedzmy stir (), getTemperature (), drink (), pour () i tym podobne. Wtedy wszyscy dekoratorzy muszą wdrożyć te metody nie z innego powodu, niż delegować je do owiniętego napoju, a Twoje MilkyBeverage i SpicyBeverage mają wszystkie te metody.

Jeśli zamiast tego masz konkretną klasę, która rozszerza lub implementuje Napoje Po prostu delegując każde wywołanie do owiniętego napoju, podklasy mogą rozszerzyć BeverageDecorator i wdrożyć tylko metody, na których im zależy, pozostawiając klasę bazową do obsługi delegacji.

To również chroni cię, jeśli Klasa napoju (lub interfejs) kiedykolwiek uzyska nową abstrakcyjną metodę: wszystko, co musisz zrobić, to dodać metodę do klasy BeverageDecorator. Bez niej musiałbyś dodać tę metodę do każdego dekoratora, który stworzyłeś.

 4
Author: Andrew,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-03-28 21:35:41

W zasadzie czasami pomijam też tę abstrakcję " średniego człowieka "(jeśli nie ma wielu kombinacji dekoratorskich). odsprzęga więcej, ale także dodaje złożoności. moim zdaniem główną ideą dekorowania jest owijanie interfejsów wewnątrz ich własnych implementacji.

 2
Author: manuel aldana,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-01-07 01:00:01

W przypadku wzorca dekoratora, dziedziczenie jest używane do dopasowania typu. Nie jest to typowy powód podkategorii. Zwykle powodem podklasowania jest dziedziczenie zachowania

Aby to rozróżnienie było jasne, sensowne jest podklasowanie napoju z CondimentDecorator, ponieważ Klasa CondimentDecorator sprawia, że rozróżnienie między implementacją napoju (jak ciemny Roast) i przyprawą (jak Mocha) jest jasne. Rozważ na przykład, że miałeś za zadanie wymyślić menu dla StarBuzz wystarczy spojrzeć na kod. Od razu dowiesz się, które napoje są "bazowe", a które przyprawy, patrząc jako klasa podstawowa. Podstawową klasą DarkRoast jest napój. Podstawową klasą Mocha jest CondimentDecorator.

Myślę jednak, że bardziej sensowne w Javie byłoby zaimplementowanie klasy beverage abstract jako interfejsu. Według książki kod nie używał tego podejścia, ponieważ StarBuzz miał już klasę abstrakcyjną (p 93) i abstrakcyjną bazę komponent jest historycznym podejściem do implementacji wzorca.

 2
Author: hgilson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-10-08 20:15:07

Klasa Basedecorator (CondimentDecorator) wymusza, aby ConcreteDecortor (Milk) miał klasę abtract base (Brevrage) w konstruktorze wejściowym.

Jeśli Klasa BaseDecorator nie istnieje, nie wymusza implementacji dekoratora, który wymaga BaseAbstractClass w input. Celem wzorca dekoratora jest wymuszenie wejścia i wyjścia klas.

(myślę, że bierzesz swój przykład z Head First Design Pattern, a w ich przykładzie BaseDecorator klasa nie ma tego contructora).

 1
Author: Normand Bedard,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-12-15 13:51:32

Może lata za późno, ale tu jest punkt z deklaracją GOF o odpowiedzialności pojedynczej.

Załóżmy, że dekorator abstrakcyjny ma również dodatkowe metody nie własne przez komponent (abstrakcyjny) tam dekarator ma wiele odpowiedzi.

Zapewnia to, że komponent jest zawsze wielokrotnego użytku, ponieważ powinien implementować wszystkie abstrakcyjne / interfejsy komponentu, ale nie może implementować dekoratora dodatkowej odpowiedzialności.

Każdy program, który używa komponent (abstract) może zapewnić, że komponent działa odpowiednio w sposób Utylizacyjny (do uruchomionego obiektu) i concrtete tworzenia komponentu (abstract). w ten sposób zapewni to integralność programu i łatwość rozwoju i rozbudowy aplikacji / programu.

W ten sposób dekorator pozostawi te alove, a następnie jakby stać jako własny przedmiot, aby być używane w jakikolwiek sposób, jak to możliwe, obejmują sposoby podobne do powyższych. np. dekorator jest dekorowany.(tylko pomysł)

Świetnie, prawda?

 0
Author: user2511680,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-08-20 20:07:57