Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: App pod kątem PHPUnit i dobrej praktyki OOP
Forum PHP.pl > Forum > PHP > Object-oriented programming
trzczy
W aplikacji jest kilka klas implementujących ten sam interfejs.

W dalszym opisie będę te klasy nazywał "tymi klasami"

Każda z tych klas ma metody, które musi posiadać ze względu na wymóg zawarty w interfejsie. Oprócz tego każda z tych klas korzysta z kilku metod, takich samych dla każdej z tych klas.

I teraz pytanie: jak udostępnić te wspólne metody dla każdej z tych klas?

Jest kilka możliwości:

1. Te klasy dziedziczą metody wspólne dla tych klas po klasie abstrakcyjnej, która te metody deklaruje.
2. Te klasy używają wspólnego trait z tymi metodami wspólnymi dla tych klas.
3. Specjalny obiekt zawierający wspólne dla tych klas metody wstrzykiwany jest do instancji każdej z tych klas.
4. Instancja każdej z tych klas wstrzykiwana jest do instancji pewnej specjalnej klasy, która posiada te metody wspólne dla tych klas.

A może jeszcze inaczej?

Mam nadzieję, że to zrozumiałe. Z góry dziękuję
lukaskolista
Co do tematu tej dyskusji: stosując TDD (red->green->refactor) to w trakcie pisania powinno Ci wyjść najlepsze rozwiązanie.

1. Zależy od konkretnego przypadku.
2. Trait to generalnie nienajlepszy pomysł.
3. I każda z tych klas proxuje te metody bez powodu? - chyba bez sensu (proxy ma sens w konkretnych przypadkach, w tym raczej nie, chociaż nie zdradziłeś szczegółów, więc ciężko powiedzieć).
4. Dekorator - w tym przypadku bez sensu, bo dekoratory stosuje się w kontekstach, a nie generycznie.

Może powiedz konkretnie o co chodzi, na pewno nikt Ci kodu nie ukranie, a tylko ułatwisz dojście do najlepszego rozwiązania, bo w tej chwili trudno cokolwiek powiedzieć.
Skie
Jeżeli dobrze rozumiem problem, pytasz jak dla N klas udostępnić współdzieloną logikę? Z tego co mówisz ta logika jest osobno oprócz metod zawartych w interfejsie, zatem wzmianka że wspóldzielą jakiś tam interfejs jest bez znaczenia, bo dotyczy innego fragmentu kodu, niż ten o który pytasz. To tylko zaciemnia przekaz pytania.

Tak naprawdę to wszystkie przedstawione przez Ciebie opcje są dopuszczalne, zgodne z OOP i TDD. Wybór jednej z nich powinien zależeć tutaj nie od implementacji, a od przeznaczenia tych klas i ich odniesienia względem budowanego przez Ciebie systemu. W opcji nr 1 mówisz o dziedziczeniu po innej klasie, opcje nr 2 i 3 to w rózny sposób wykonana kompozycja, a 4 to strategia. Jeżeli głównym celem działania tych klas, jest ta wspóldzielona logika, a metody interejsu definiują w jaki sposób klasy ją spełniają, wtedy powinieneś użyć strategii. Jeśli jednak głownym celem jest interfejs, a wspóldzielona logika to różnego rodzaju helpery, czy też metody należące do innego interfejsu wtedy raczej obstawałbym za opcją dziedziczenia lub kompozycji. Tutaj z kolei, jeśli mozesz użyć dziedziecznia - użyj go - jeśli jest to niemożliwe - wtedy kompozycja. Wybór pomiędzy użyciem trait lub wstrzykiwaniem dodatkowego obiektu w przypadku kompozycji to pytanie dotyczące, które z tych dwóch metod pozwoli Ci na łatwiejszy maintenance istniejącego systemu i która z tyhc metod będzie konsystenatna z tym co już istnieje.

TLDR - pod wzgldem OOP i TDD wszystkie 4 metody są tak samo akceptowalne, a wybór leży po stronie projektowej, a nie stricte technologicznej.
lukaskolista
Czy 4. to strategia - na pierwszy rzut oka tak, ale po przemyśleniu wydaje mi się, że autorowi chodzi tutaj o dekorator, tylko źle to opisał. Czemu tak mi się wydaje? Ponieważ współdzielona logika jest jedna, a strategia zakłada, że każda klasa implementuje daną metodę inaczej.

Najlepiej jak poda szczegóły, to będzie łątwiej.
trzczy
Napisałem wyjaśnienie na prośbę @lukaskolista, ale straciłem internet ma pewien czas. Kiedy odzyskałem, pojawił się komentarz @Skie. Z tego co czytam, Skie zrozumiał o co biega, i faktycznie ten temat interface jest tu raczej bez znaczenia. Istotne jest to, że kilka klas korzysta z tej samej logiki, tych samych metod.

Przy okazji: co to jest strategia? Albo gdzie o tym czytać.




Odpowiedź dla @lukaskolista zasejwowana przed utratą internetu: ;-)
Ta aplikacja zajmuje się ustalaniem kolejności grup różnych rzeczy. Przykładowo stringów albo obiektów stdClass, albo liczb, albo tablic.

Danymi wejściowymi są te rzeczy oraz info, że niektóre rzeczy są poprzedzone przez inne rzeczy. Jest pewien algorytm matematyczny, który na podstawie tej info ustala grupy rzeczy i kolejność tych grup. Zatem wypluwana jest tablica tablic z tymi rzeczami.


W zależności od tego, jakie to są rzeczy, tworzona jest inna klasa, implementująca ten sam interfejs. Załóżmy, że zajmujemy się sortowaniem pewnych obiektów stdClass. Wtedy tworzymy klasę np. MyStdClassObjects implementującą interfejs, który narzuca zadeklarowanie metody isLike.

Metoda isLike jest to metoda, dzięki której można określać tożsamość poszczególnych rzeczy typu stdClass. Jest ona porzebna, aby właściwie intepretować informację o tym, że jakiś obiekt stdClass jest poprzedzony innym obiektem stdClass.

Gdybyśmy zajmowali się sortowaniem liczb, wymagana interfejsem metoda isLike by po prostu sprawdzała równość liczb.

Ale wróćmy do sortowania obiektów stdClass i do klasy MyStdClassObjects. Z zasobów tej klasy korzysta metoda create, która pozwala uzyskać końcową tablicę tablic. Metoda create jest literalnie ta sama bez względu na to, co sortujemy: czy liczby, czy stringi, czy obiekty itd. Metoda create korzysta z właściwej metody isLike. Poza tym korzysta z kilku metod pomocniczych, które są takie same niezależnie od tego, co sortujemy. Jedną z takich metod jest metoda hasFormerThings, która mówi, czy dana rzecz następuje po innej rzeczy.

Zatem widać, że są metody wspólne, takie same dla każdej implementacji interfejsu. I pytanie brzmi, jak te metody mają się komunikować z klasą implementującą ten interfejs, czyli np. z klasą MyStdClassObjects. Gdzie mają być zgrupowane. Jak mają być udostępnione tej klasie?
Skie
Jak dla mnie to co opisujesz brzmi jak typowe zastosowanie dla strategii.

Teoria: https://en.wikipedia.org/wiki/Strategy_pattern
Strona opisująca implementację tego wzorca: http://www.oodesign.com/strategy-pattern.html
Trochę prostsza wersja tego co powyżej w PHP: http://www.phptherightway.com/pages/Design-Patterns.html
trzczy
No to już wiem, co to jest strategia. Dzięki. Rzeczywiście w mej aplikacji jest strategia, niemniej, jak już się wcześniej zgodziliśmy, pytanie raczej dotyczy dostępu poszczególnych klas klienckich do wspólnej logiki.

Zrobiłem to na zasadzie użycia Trait przez każdą z klas klienckich, ale nie wiem, jakie to niesie ze sobą skutki dla TDD, bo nie mam doświadczenia. Wybrałem Trait, gdyż chciałem sobie zagadnienie Trait poćwiczyć. Wniosek z dyskusji, że co do dostępu do tej logiki, to jest kilka równorzędnych dróg, chyba, że "w praniu" wynikną jakieś zalety jednej z nich.
To jest wersja lo-fi głównej zawartości. Aby zobaczyć pełną wersję z większą zawartością, obrazkami i formatowaniem proszę kliknij tutaj.
Invision Power Board © 2001-2025 Invision Power Services, Inc.