Witaj Gościu! ( Zaloguj | Rejestruj )

Forum PHP.pl

 
Reply to this topicStart new topic
> Kompozycja vs dziedziczenie
lukaskolista
post
Post #1





Grupa: Zarejestrowani
Postów: 872
Pomógł: 94
Dołączył: 31.03.2010

Ostrzeżenie: (0%)
-----


Cześć, mam problem z decyzją, czy zastosować kompozycję czy dziedziczenie.

Stan aktualny:
MatchInterface (interface)
Match (class, implementuje MatchInterface)

Chciałbym stworzyć nowy rodzaj Matcha (dopasowania), który zyskuje nową metodę "getName()" oraz argument konstruktora względem klasy Match. W tym celu dodałem nowy interface oraz klasę:
NamedMatchInterface (interface dziedziczący po MatchInterface dodający nową metodą getNamed())
NamedMatch (class, implementuje NamedMatchInterface)

Proszę nie zwracać uwagi na na nazewnictwo, jest to PSR ale nie wykluczone, że zmienię tę konwencję, bo mi trochę nie odpowiada.

Moje pytanie brzmi: klasa "NamedMatch" powinna dziedziczyć po klasie "Match" i dodawać nową metodę oraz nadpisać konstruktor, czy jednak zastosować kompozycję?
Z punktu widzenia testów jednostkowych oraz możliwości rozwoju skłaniam się do kompozycji, jednak chciałbym poznać Wasze zdanie.

Edit:
Niby to powinna być kompozycja, bo dochodzi nowa metoda, jednak 4 inne metody są identyczne jak w klasie Match i w przypadku zastosowania kompozycji będą po prostu proxowane do klasy Match.

Ten post edytował lukaskolista 4.12.2016, 14:24:52
Go to the top of the page
+Quote Post
rafkon1990
post
Post #2





Grupa: Zarejestrowani
Postów: 37
Pomógł: 9
Dołączył: 14.09.2016
Skąd: Śląskie

Ostrzeżenie: (0%)
-----


Kompozycję stosuje się wtedy, gdy między klasami zachodzi relacja typu „całość ↔ część” tzn. nowa klasa zawiera w sobie istniejącą klasę.
Dziedziczenie stosuje się wtedy, gdy między klasami zachodzi relacja „generalizacja ↔ specjalizacja” tzn. nowa klasa jest szczególnym rodzajem już istniejącej klasy.

Myślę, że w twoim przypadku jest dziedziczenie.
Go to the top of the page
+Quote Post
lukaskolista
post
Post #3





Grupa: Zarejestrowani
Postów: 872
Pomógł: 94
Dołączył: 31.03.2010

Ostrzeżenie: (0%)
-----


Dokładnie na odwrót - kompozycja. Nowa metoda nie jest specjalizacją klasy, a dodatkową funkcjonalnością.
Poza tym trafiłeś na jedną z licznych zasad prezentowanych na blogach.

Klasy Match i NamedMatch są na tym samym poziomie specjalizacji, przy czym NamedMatch ma jedną funkcjonalność więcej, reszta jest wspólna.

Ten post edytował lukaskolista 4.12.2016, 15:16:41
Go to the top of the page
+Quote Post
Pyton_000
post
Post #4





Grupa: Zarejestrowani
Postów: 8 068
Pomógł: 1414
Dołączył: 26.10.2005

Ostrzeżenie: (0%)
-----


NamedMatchInterface powinien rozszerzać MatchInterface, a klasy implementować odpowiedni interface.
Go to the top of the page
+Quote Post
lukaskolista
post
Post #5





Grupa: Zarejestrowani
Postów: 872
Pomógł: 94
Dołączył: 31.03.2010

Ostrzeżenie: (0%)
-----


Cytat
NamedMatchInterface powinien rozszerzać MatchInterface, a klasy implementować odpowiedni interface.

Bez urazy, ale co to ma do tematu? Napisałeś to, co ja napisałem już na samym początku tego wątku.

MatchInterface -> Match
MatchInterface -> NamedMatchInterface -> NamedMatch

Chodzi o to, czy zastosować kompozycję, czy dziedziczenie.

Ten post edytował lukaskolista 4.12.2016, 15:29:17
Go to the top of the page
+Quote Post
Pyton_000
post
Post #6





Grupa: Zarejestrowani
Postów: 8 068
Pomógł: 1414
Dołączył: 26.10.2005

Ostrzeżenie: (0%)
-----


Ok spoko. Ja bym zastosował dziedziczenie. Nie warto chyba w tym przypadku komplikować sobie życia kompozycją.
Go to the top of the page
+Quote Post
rafkon1990
post
Post #7





Grupa: Zarejestrowani
Postów: 37
Pomógł: 9
Dołączył: 14.09.2016
Skąd: Śląskie

Ostrzeżenie: (0%)
-----


Cytat(lukaskolista @ 4.12.2016, 15:14:45 ) *
Dokładnie na odwrót - kompozycja. Nowa metoda nie jest specjalizacją klasy, a dodatkową funkcjonalnością.
Poza tym trafiłeś na jedną z licznych zasad prezentowanych na blogach.

Klasy Match i NamedMatch są na tym samym poziomie specjalizacji, przy czym NamedMatch ma jedną funkcjonalność więcej, reszta jest wspólna.


Edit. Zerknąłem do googla i masz rację - jest to kompozycja.

Ten post edytował rafkon1990 4.12.2016, 17:57:53
Go to the top of the page
+Quote Post
lukaskolista
post
Post #8





Grupa: Zarejestrowani
Postów: 872
Pomógł: 94
Dołączył: 31.03.2010

Ostrzeżenie: (0%)
-----


Teraz jeszcze zostaje kwestia interfaceow. Czy NamedMatchInterface moze dziedziczyc po MatchInterface, czy jednak powinien specyfikować kontrakt na nowo? Co do tego to nie mam pojecia bo kompozycje rozpatruje się w kontekście implementacji a nie abstrakcji, przynajmniej o abstrakcji nic nie mogę znaleźć.
Go to the top of the page
+Quote Post
Pyton_000
post
Post #9





Grupa: Zarejestrowani
Postów: 8 068
Pomógł: 1414
Dołączył: 26.10.2005

Ostrzeżenie: (0%)
-----


Raczej powinien dziedziczyć, tym bardziej że kontrakt jest taki sam wspólny.
Go to the top of the page
+Quote Post
lukaskolista
post
Post #10





Grupa: Zarejestrowani
Postów: 872
Pomógł: 94
Dołączył: 31.03.2010

Ostrzeżenie: (0%)
-----


Chyba tak jak piszesz interface powinien dziedziczyć, bo opisuje kontrakt, a nie implementację, natomiast implementacja powinna być zrealizowana za pomocą kompozycji.

Tak z ciekawości zapytam:
Jak testujecie klasy, które po sobie dziedziczą? Przykładowo mamy klasę A z metodą x() i y() + mamy testy do tej klasy i metody. Do tego mamy klasę B też z metodą x(), która jest odziedziczona z klasy A, natomiast metoda y() jest inna. Jak testujecie klasę B? Piszecie testy dla metody x() w obu klasach? Dodatkowo co w przypadku, gdy klasa potomna korzysta z metody rodzica + dodaje swoją logikę - nie da się zamockować klasy rodzica, bo dziecziczenie to mechaizm języka.
Go to the top of the page
+Quote Post
Pyton_000
post
Post #11





Grupa: Zarejestrowani
Postów: 8 068
Pomógł: 1414
Dołączył: 26.10.2005

Ostrzeżenie: (0%)
-----


Normalnie. Testujesz klasę której używasz. Więc jeśli używasz B to testujesz metody których używasz. Ciebie nie interesuje że w B jest tylko zmieniona Y a X jest brana z A, Ciebie interesuje wynik metody. Bo jeśli zmieni się metoda A::X to Twoj kod przestaje działać.
Go to the top of the page
+Quote Post
Pilsener
post
Post #12





Grupa: Zarejestrowani
Postów: 1 590
Pomógł: 185
Dołączył: 19.04.2006
Skąd: Gdańsk

Ostrzeżenie: (0%)
-----


Cytat
czy zastosować kompozycję czy dziedziczenie.
- pytanie dziwne, bo to dwa przeciwstawne wzorce.

I "część wspólna" to nie jest powód, by dziedziczyć czy tym bardziej tworzyć kompozycję. To wg mnie podstawowy błąd. Tak się robiło w XIX wieku, gdzie programy były statyczne i pisane z myślą o tym, że nigdy nie będą zmieniane, W dobie adżajlów używanie wzorców, które bazują na bardzo sztywnej relacji pomiędzy obiektami (jak dziedziczenie) jest sztuką dla sztuki. Dziedziczenie pięknie wygląda tylko w teorii, w praktyce zaraz zmieni się w stertę antywzorców takich jak jojo czy callsuper.

Trzeba zawsze dążyć do tego, aby implementacja była jak najprostsza i jak najbardziej skalowalna.
Jeśli mamy część wspólną to wystarczy:
- utworzyć klasę np. MatchService (część wspólna)
- dostarczyć ją do klas Match i NamedMatch (które implementują część wspólną, oraz korzystają z jej funkcjonalności)
Czyli prosta fasada. Dzięki temu możemy dowolnie rozwijać każdą klasę nie martwiąc się, że np. złamiemy kontrakt klasy nadrzędnej. Wiele frameworków ma wręcz zautomatyzowane tworzenie fasad, gdzie fasadę wpisuje się w .cfg i już mamy gotowy do użycia obiekt Match lub NamedMatch wraz ze wszystkimi zależnościami.
Go to the top of the page
+Quote Post
lukaskolista
post
Post #13





Grupa: Zarejestrowani
Postów: 872
Pomógł: 94
Dołączył: 31.03.2010

Ostrzeżenie: (0%)
-----


Błędnie założyłeś, że te obiekty są usługami. Kompozycja jest przestarzała? Od kiedy?

Zrobiłem to tak:
MatchInterface -> Match (class)
NamedMatchInterface -> NamedMatch (class)

NamedMatchInterface posiada metody:
getName(): string
getMatch(): MatchInterface

Ten post edytował lukaskolista 5.12.2016, 10:47:19
Go to the top of the page
+Quote Post
com
post
Post #14





Grupa: Zarejestrowani
Postów: 3 034
Pomógł: 366
Dołączył: 24.05.2012

Ostrzeżenie: (0%)
-----


@offtop

PSR nie definiuje standardu nazewnictwa klas, interfejsów itd (IMG:style_emoticons/default/smile.gif)

Wspomniane było o tym kiedyś, ale w kontekście nazewnictwa dla samego PSR, a w projekcie można przyjąć sobie jaki chcesz standard, ja ostatnio preferuje bez suffixów, według mnie tak jest lepiej:
  1. class Foo
  2. {
  3. protected $bar;
  4. public function __construct(Bar $bar)
  5. {
  6. $this->bar = $bar;
  7. }
  8. }
  9.  
  10. $foo = new Foo(new SimpleBar());

niż:
  1. class Foo
  2. {
  3. protected $bar;
  4. public function __construct(BarInterface $bar)
  5. {
  6. $this->bar = $bar;
  7. }
  8. }
  9.  
  10. $foo = new Foo(new SimpleBar());

Bo tak jasno mówisz że to jest obiekt danego typu a nie jakiś interfejs
Go to the top of the page
+Quote Post
Pyton_000
post
Post #15





Grupa: Zarejestrowani
Postów: 8 068
Pomógł: 1414
Dołączył: 26.10.2005

Ostrzeżenie: (0%)
-----


Widzisz... I tu jest problem (IMG:style_emoticons/default/smile.gif)
Bo wg. Ciebie oczekujesz Obiektu konkretnego typu.
Dodając sufix widzisz że oczekujesz też Typu, ale już widzisz że Obiekt Może być Potter implementujący Interface. Nie mając Sufixu musisz się posiłkować dokumentacją żeby wiedzieć czy to co tam jest to Interface czy konkretny obiekt.

A co jeśli nazwiesz Interface Book, a potem chcesz klasę Book? Bo robisz z niej np. dziedziczenie?

Interface, Trait, abstract to wszystko powinno być dodawane do nazwy.
Go to the top of the page
+Quote Post

Reply to this topicStart new topic
2 Użytkowników czyta ten temat (2 Gości i 0 Anonimowych użytkowników)
0 Zarejestrowanych:

 



RSS Aktualny czas: 20.12.2025 - 21:40