Inheritance vs. interface dla takich samych metod., Klasy dziedziczące mają te same metody. Powinny dziedziczyć? |
Inheritance vs. interface dla takich samych metod., Klasy dziedziczące mają te same metody. Powinny dziedziczyć? |
12.12.2015, 13:32:56
Post
#1
|
|
Grupa: Zarejestrowani Postów: 460 Pomógł: 49 Dołączył: 5.06.2011 Ostrzeżenie: (0%) |
Kiedy klasy dziedziczące mają takie same metody, to lepiej by dziedziczyły te metody od klasy nadrzędnej, czy miały te metody "przykazane" przez interface i umieszczone w sobie? Takie 2 rozwiązania pokazuję poniżej. Które jest bardziej poprawne?
Z góry dziękuję |
|
|
12.12.2015, 14:43:14
Post
#2
|
|
Grupa: Zarejestrowani Postów: 98 Pomógł: 33 Dołączył: 10.05.2011 Skąd: Krak Ostrzeżenie: (0%) |
W przykładzie, który podałeś klasy są identyczne dlatego nie ma sensu ich rozbijać na dwie. Jeśli część metod byłaby wspólna (dokładnie takie same metody łącznie z implementacją), wtedy te metody mogłyby być w klasie nadrzędnej, a w klasach pochodnych umieściłbyś różnice.
W twoim przypadku powinna to być jedna klasa. Czy interfejs byłby tam przydatny to zależy. Można by napisać więcej gdybyśmy znali szerszy kontekst, jaką aplikację piszesz i jak obiekty tej klasy są używane. |
|
|
12.12.2015, 15:32:26
Post
#3
|
|
Grupa: Zarejestrowani Postów: 460 Pomógł: 49 Dołączył: 5.06.2011 Ostrzeżenie: (0%) |
W przykładzie, który podałeś klasy są identyczne dlatego nie ma sensu ich rozbijać na dwie. No rzeczywiście. Kontekst mojego pytania jest taki, że czytam sobie tutorial o design patterns i tam napotkałem na takie "zażądanie" metod w klasie abstrakcyjnej, po czym w klasach dziedziczących zastosowane identyczne metody. Pomyślałem, że te identyczne metody mogłyby być jedynie dziedziczone z klasy abstrakcyjnej, a nie deklarowane w każdej klasie konkretnej. (Tu w przykładzie zastosowałem interface, a nie klasę abstrakcyjną, co w sumie byłoby chyba analogią). Link do tutoriala: https://wwphp-fb.github.io/faq/object-orien...stract-factory/ Definicja klasy abstrakcyjnej wygląda tak:
Cały kod tak:
Ten post edytował trzczy 12.12.2015, 15:33:46 |
|
|
12.12.2015, 15:41:38
Post
#4
|
|
Grupa: Zarejestrowani Postów: 361 Pomógł: 22 Dołączył: 10.02.2015 Ostrzeżenie: (0%) |
Tak moimi słowami, klasę abstrakcyjną czy interfejs piszę często po to, aby mieć pewność, że dany obiekt posiada implementację danej metody.
Przykładowo masz np. klasę abstrakcyjną Storage obsługującą zapisywanie danych, po której dziedziczą np. klasy Database, Session, File itp. Każda z nich musi mieć metodę odczytującą i zapisującą. Więc w klasie nadrzędnej Storage umieszczam dwie metody write i read. Potem gdy, inna klasa będzie musiała przyjąć obiekt danych, to w metodzie rzutuję I mam pewność że będę mógł odczytać i zapisać dane. Przykład taki z rękawa, może wiesz co mam na myśli. Ten post edytował Fred1485 12.12.2015, 15:42:37 -------------------- |
|
|
12.12.2015, 19:00:55
Post
#5
|
|
Grupa: Zarejestrowani Postów: 1 268 Pomógł: 254 Dołączył: 11.06.2009 Skąd: Świętochłowice Ostrzeżenie: (0%) |
Osobiście spotkałem się z podejściem, gdzie interfejsy służyły do zdefiniowania publicznego API konkretnej klasy obiektów (np Storage), natomiast klasa abstrakcyjna była podstawową implementacją tego interfejsu, zawężając jego zakres do podklasy tych obiektów (np DBStorage). Po tej klasie dziedziczyły już konkretne klasy, zmieniając jedynie to, co trzeba (np MySQLStorage).
-------------------- ★Mój blog || Okiem krytyka★
|
|
|
12.12.2015, 22:37:35
Post
#6
|
|
Grupa: Zarejestrowani Postów: 460 Pomógł: 49 Dołączył: 5.06.2011 Ostrzeżenie: (0%) |
Dzięki za odpowiedzi. Dla mnie wynika z tego, że nie ma "lepszego" sposobu, i wybór należy do programisty. Albo pokazuje on w klasie nadrzędnej lub interfejsie, jakie metody będą wymagane w kasach dziedziczących względnie implementujących interfejs, albo kieruje się rozsądkiem i stosuje dziedziczenie identycznych metod zamiast je powielać.
Oczywiście metoda 1. ma też swoje zalety, bo kod jest wtedy czytelniejszy. |
|
|
13.12.2015, 00:36:28
Post
#7
|
|
Grupa: Zarejestrowani Postów: 2 355 Pomógł: 533 Dołączył: 15.01.2010 Skąd: Bydgoszcz Ostrzeżenie: (0%) |
To nie jest kwestia żadnego wyboru. Do czego innego służy interfejs, a do czego innego klasa abstrakcyjna. I nie masz tu żadnego wyboru, czy jeszcze lepiej "lepszego rozwiązania".
W interfejsach umieszczasz informację o tym jakie metody muszą być zawarte w klasie, która go implementuje i tyle, mogą to być zupełnie nie powiązane ze sobą klasy, które implementują ten sam interfejs. Na dodatek interfejs nie może zawierać żadnej funkcjonalności. Klasa abstrakcyjna, może mieć jakąś funkcjonalność w sobie, a resztę zostawia klasom dziedziczącym po niej. Na dodatek każda klasa dziedzicząca, musi mieć z nią dość ścisły związek. Podsumowując implementacja i dziedziczenie są od siebie niezależne i nic nie stoi na przeszkodzie, implementować interfejs i dziedziczyć po danej klasie jednocześnie. Czy też tylko implementować interfejs, czy też tylko dziedziczyć po klasie abstrakcyjnej. Twój abstrakcyjny przykład jest bez sensu i nie rozumiesz idei obiektowości. Przykład z tutoriala jest sensowny, mógłbyś sobie do niego jeszcze napisać interfejsy i byłoby też dobrze, niektórzy powiedzą, że nawet lepiej. |
|
|
13.12.2015, 01:07:19
Post
#8
|
|
Grupa: Zarejestrowani Postów: 460 Pomógł: 49 Dołączył: 5.06.2011 Ostrzeżenie: (0%) |
Damonsson, nie rozumiesz pytania. Chodzi o dublowanie metod. W tutorialu metody są dublowane, a można tego tego uniknąć przez dziedziczenie tych metod z klasy nadrzędnej. Nie chodzi mi o róznice między abstract class a interface, bo jest mi dobrze znana. Z dyskusji powyżej wynika, że programista sam decyduje, czy lepiej będzie uzyskać czytelność kodu przez zastosowanie abstract class lub interface, czy zastosować dziedziczenie metod i uniknąć dublowania.
|
|
|
13.12.2015, 10:42:44
Post
#9
|
|
Grupa: Zarejestrowani Postów: 6 365 Pomógł: 1114 Dołączył: 30.08.2006 Ostrzeżenie: (0%) |
Ale Damansson dobrze Ci odpowiedział i według mnie nie zrozumialeś do końca jego wypowiedzi. Nijak się ma dublowanie metod bo w interface jest tylko ich czysta definicja. Klasa abstrakcyjna może zawierać część funkcjonalności wspólną dla wszystkich klas potomnych ale to do klas poszczególnych zależy jak te specyficzne metody (abstrakcyjne) obsłużą. Masz jeszcze cechy (traits) o których tu nic nie mówisz a które możesz dołączyć do kodu również eliminując powielanie.
Zobacz też interface w takim kontekście: https://github.com/RalfEggert/zend-expressi...eateFactory.php -------------------- |
|
|
13.12.2015, 18:42:16
Post
#10
|
|
Grupa: Zarejestrowani Postów: 460 Pomógł: 49 Dołączył: 5.06.2011 Ostrzeżenie: (0%) |
Ale Damansson dobrze Ci odpowiedział i według mnie nie zrozumialeś do końca jego wypowiedzi. Nie odpowiedział na moje pytanie. Jego wypowiedź zrozumiałem, aleta wypowiedź nie uczyniła mnie bogatszym w wiedzę, ani nie odpowiedziała na problem.Nijak się ma dublowanie metod bo w interface jest tylko ich czysta definicja. W interface nie ma definicji metod. Są nazwy metod. Klasa abstrakcyjna może zawierać część funkcjonalności wspólną dla wszystkich klas potomnych ale to do klas poszczególnych zależy jak te specyficzne metody (abstrakcyjne) obsłużą. Nie pytam o to, co to jest klasa abstrakcyjna, bo wiem jak ona działa.Słuchajcie, ja nie pytam, co mogę jeszcze zrobić, jak ten tutorial pozmieniać itp. Powtarzam pytanie. Czy powielanie definicji metod w klasach podrzędnych, we wskazanym tutorialu, można uznać za wadę tego kodu, w świetle tego, że można takie identyczne definicje metod dziedziczyć z klasy nadrzędnej. Po prostu, wydaje mi się, że w informatyce jednak nie powinno się powielać tego samego kodu. |
|
|
13.12.2015, 18:53:29
Post
#11
|
|
Grupa: Zarejestrowani Postów: 1 268 Pomógł: 254 Dołączył: 11.06.2009 Skąd: Świętochłowice Ostrzeżenie: (0%) |
Cytat W interface nie ma definicji metod. Są nazwy metod. Czyli są definicje… To, co jest w klasie abstrakcyjnej (oczywiście jeśli metody mają ciało), to już implementacja, nie definicja. Co do kodu w tutorialu: osobiście zamieniłbym tę klasę abstrakcyjną na interfejs, jeśli i tak nie podaję podstawowej implementacji tych metod. -------------------- ★Mój blog || Okiem krytyka★
|
|
|
13.12.2015, 19:08:52
Post
#12
|
|
Grupa: Zarejestrowani Postów: 8 068 Pomógł: 1414 Dołączył: 26.10.2005 Ostrzeżenie: (0%) |
O jakim dublowaniu mówisz. Bo tutaj jedynie dublowanie gettery.
Jeśli jednak pisząc ogólnie to: klasa nadrzędna zawsze powinna zawierać zestaw metod wspólnych. Czy to będzie klasa abstract czy zwyczajna to już zależy od preferencji. Czy paretn ma być implements Intreface to też zależy. Inny przypadek to jak ktoś wcześniej napisał Interface jako definicja metod (Przytoczony przykład z Storage). |
|
|
13.12.2015, 22:18:07
Post
#13
|
|
Grupa: Zarejestrowani Postów: 98 Pomógł: 33 Dołączył: 10.05.2011 Skąd: Krak Ostrzeżenie: (0%) |
trzczy, Damansson ma rację, w tutorialu żadne metody nie są dublowane. Wydaje się, że rzeczywiście nie zrozumiałeś jeszcze dobrze programowanie obiektowego.
AbstractFactory jest interfejsem czyli nie posiada implementacji. Zawiera tylko deklarację metod (deklarację nie definicję bo definicja to implementacja metody). Każda klasa implementująca interfejs AbstractFactory definiuje metody zadeklarowane w tym interfejsie, ale nie jest to duplikacja. Dzięki temu masz dwie różne implementacje tego samego interfejsu. To znaczy możesz używać obiektu dowolnej z klas pochodnych w dokładnie ten sam sposób, chociaż każda klasa będzie zachowywać się trochę inaczej (czyli korzystasz z polimorfizmu). Zaleta tego jest taka, że kod który używa obiektu dowolnej z klas implementujących ten interfejs nie musi brać pod uwagę, z jaką klasą konkretnie ma do czynienia. Kod przez to będzie prostszy w zrozumieniu, rozbudowie i testowaniu. Ktoś u góry podawał już przykład z klasą Storage. Powiedzmy że tworzysz aplikację, która zapisuje jakieś wiadomości do plików lub bazy. Kod nie był testowany i może zawierać błędy.
Ten przykład pokazuje, jak łatwo można zmieniać sposób zapisu wiadomości w aplikacji. Wystarczy zmienić jedną linijkę (stworzyć obiekt klasy DbStorage zamiast FileStorage), a obiekty RequestHandler będą zapisywać wiadomości do bazy danych zamiast do plików. Chcesz zmienić sposób generowania wiadomości? Znowu zmieniasz jedną linijkę ARequestHandler na BRequestHandler. Obiekty StorageInterface i AbstractRequestHandler możesz tworzyć np. na podstawie pliku konfiguracyjnego przy stracie aplikacji i dalej nie obchodzi Cie z jakiej implementacji korzystasz. Przez to możesz zmieniać zachowanie aplikacji w pliku konfiguracyjnym. Klasa zajmująca się zapisem do bazy jest interfejsem, ponieważ jedyną wspólną rzeczą dla wszystkich klas zapisujących wiadomości jest to, że aby zapisać wiadomość trzeba wywołać metodą save z jednym argumentem(php 5.x niestety nie pozwala ograniczyć typu argumentu do stringa w deklaracji metody). AbstractRequestHandler jest klasą abstrakcyjną, ponieważ jej klasy pochodne mają wspólny kod (konstruktor). Zauważ że sposób zapisu i sposób generowania wiadomości zmieniasz niezależnie, dzięki temu że wydzieliliśmy te dwie odpowiedzialności od siebie. Gorszym rozwiązaniem byłoby definiowanie sposobu generowania wiadomości i zapisu w jednej klasie, wtedy moglibyśmy mieć klasy ARequestHandlerFileStorage, ARequestHandlerDbStorage, BRequestHandlerFileStorage itd. Zanim zacznie się uczyć wzorców projektowych, warto dowiedzieć się trochę na temat ogólnych zasad programowania obiektowego albo kupić dobrą książkę na temat wzorców. W internecie nigdy nie trafiłem na źródła z dobrym wprowadzeniem do programowanie obiektowego, nigdy też nie widziałem w internecie dobrych przykładów. Jeszcze co do interfejsów: to pokazuje gdzie mają przewagę nad klasami abstrakcyjnymi przykłady są w javie ale każdy powinien zrozumieć. Ten post edytował droslaw 13.12.2015, 23:24:01 |
|
|
14.12.2015, 08:28:08
Post
#14
|
|
Grupa: Zarejestrowani Postów: 460 Pomógł: 49 Dołączył: 5.06.2011 Ostrzeżenie: (0%) |
O jakim dublowaniu mówisz. Bo tutaj jedynie dublowanie gettery. Mówię konkretnie o tym właśnie dublowaniu. Damansson ma rację, w tutorialu żadne metody nie są dublowane. Zdaje się, że 2 osoby zauważyły, że są dublowane. I pojawia się nam problem, co to znaczy definicja metody. Dla mnie definicja metody to jest to samo co deklaracja metody. I z tego co widzę, to sugerujecie, że definicją metody jest na przykład samo coś takiego:
Zatem mielibyśmy definicję metody, która nie określa, jak działa metoda, czyli nie definiuje metody w sensie matematycznym. Jest to dla mnie trudne do przyjęcia, i w sumie nie wiem co z tym zrobić. Pamiętam, że kiedyś coś kodowałem w jakimś języku programowania, gdzie było słowo kluczowe 'defun nazwafunkcji' i dalej następowało body funkcji. Ale jeśli w oop rzeczywiście definicja nie musi definiować, to może niepotrzebnie poprawiałem Kolegę powyżej w jednym z postów. Nie będę się szczegółowo odnosił do wszystkiego w tym wątku, ale ogólnie pojawia mi się obraz i wnioski, za co Wam dziękuję. Ten post edytował trzczy 14.12.2015, 08:31:54 |
|
|
14.12.2015, 08:33:13
Post
#15
|
|
Grupa: Zarejestrowani Postów: 8 068 Pomógł: 1414 Dołączył: 26.10.2005 Ostrzeżenie: (0%) |
Settery i gettery to nie problem. O ile nie mają std. zadania to prawie każde IDE wygeneruje Ci taką listę.
Ad. definicji w programowaniu jest definicja/deklaracja i implementacja. |
|
|
14.12.2015, 12:16:07
Post
#16
|
|
Grupa: Zarejestrowani Postów: 98 Pomógł: 33 Dołączył: 10.05.2011 Skąd: Krak Ostrzeżenie: (0%) |
I pojawia się nam problem, co to znaczy definicja metody. Dla mnie definicja metody to jest to samo co deklaracja metody. I z tego co widzę, to sugerujecie, że definicją metody jest na przykład samo coś takiego:
Kiedyś bawiłem się w C++, tam rozróżnienie miedzy definicją i deklaracją jest wyraźne: What is the difference between a definition and a declaration?. Java: What is the difference between declaration and definition in Java? (PHP jest pod tym względem podobny do Javy). Dlatego dla mnie deklaracja to nie koniecznie definicja. A może lepiej używać pojęć interfejs i implementacja. Warto byłoby ustalić wspólne nazewnictwo bo wtedy będzie mniej nieporozumień i chętnie dowiem się co jeszcze inni na ten temat myślą. Zatem mielibyśmy definicję metody, która nie określa, jak działa metoda, czyli nie definiuje metody w sensie matematycznym. Jest to dla mnie trudne do przyjęcia, i w sumie nie wiem co z tym zrobić. Pamiętam, że kiedyś coś kodowałem w jakimś języku programowania, gdzie było słowo kluczowe 'defun nazwafunkcji' i dalej następowało body funkcji. Ale jeśli w oop rzeczywiście definicja nie musi definiować, to może niepotrzebnie poprawiałem Kolegę powyżej w jednym z postów. Czyli w interfejsie masz metody bez implementacji a implementację dajesz dopiero klasach implementujących ten interfejs. Jest to jedna z najważniejszych cech programowania obiektowego. Dobrze że uczysz się wzorców ale lepiej byłoby gdybyś najpierw dobrze zrozumiał ogólne zasady dotyczące programowania obiektowego. |
|
|
15.12.2015, 08:00:27
Post
#17
|
|
Grupa: Zarejestrowani Postów: 144 Pomógł: 0 Dołączył: 22.03.2015 Ostrzeżenie: (0%) |
W przykładzie gdzie implementujesz interfejs chodzi o to, że w interfejsie masz określone jakie metody musza być implementowane w danym obiekcie. W Twoim przykładzie pokazałeś dwa obiekty implementujące ten interfejs, a te obiekty są różne to że one zawierają te metody to nie oznacza że są identyczne, bo poniżej mogą zawierać inne metody które coś tam robią. Interfejs określa Ci czego potrzebujesz aby stworzyć dany obiekt który np. będzie wstrzykiwany do innej metody lub obiektu i wtedy będziesz miał wpisane na przykład tak :
public function example(TwojInterfejs $service){ .. } |
|
|
17.12.2015, 01:13:47
Post
#18
|
|
Grupa: Zarejestrowani Postów: 460 Pomógł: 49 Dołączył: 5.06.2011 Ostrzeżenie: (0%) |
Jeszcze się gubię w tym branżowym słownictwie...
public function example(TwojInterfejs $service){ .. } TwojInterfejs to jest type hinting i jednocześnie nazwa wymaganej klasy? Słowa 'Interfejs' używacie w znaczeniu 'interface', czyli słowa kluczowego php czy może w znaczeniu API? Sorka pewnie kaleczę zwyczajowy żargon. Ten post edytował trzczy 17.12.2015, 01:15:17 |
|
|
17.12.2015, 10:06:18
Post
#19
|
|
Grupa: Zarejestrowani Postów: 8 068 Pomógł: 1414 Dołączył: 26.10.2005 Ostrzeżenie: (0%) |
Możesz nazwać jak chcesz np:
StorageInterface StorageI IStorage InterfaceStorage Dowolność, chodzi o to żeby wiedzieć że Obiekt docelowy wkładany do metody musi być typu lub implementować interface. |
|
|
17.12.2015, 12:35:52
Post
#20
|
|
Grupa: Zarejestrowani Postów: 460 Pomógł: 49 Dołączył: 5.06.2011 Ostrzeżenie: (0%) |
Dowolność, chodzi o to żeby wiedzieć że Obiekt docelowy wkładany do metody musi być typu lub implementować interface. Zatem przykładowo taki type hint 'InterfaceStorage' musi być wcześniej zadeklarowany w ten sposób: class InterfaceStorage { //some code here } lub interface InterfaceStorage { //some code here } tak łopatologicznie? |
|
|
Wersja Lo-Fi | Aktualny czas: 28.04.2024 - 13:18 |