Kiedy używasz koncepcji polimorfizmu, tworzysz hierarchię klas, a korzystając z referencji rodziców wywołujesz funkcje interfejsu, nie wiedząc, który konkretny typ ma obiekt. To jest świetne. Przykład:
Masz kolekcję zwierząt i przywołujesz funkcje wszystkich zwierząt eat
i nie obchodzi cię, czy to pies je czy kot. Ale w tej samej hierarchii klas masz zwierzęta, które mają dodatkowe - inne niż odziedziczone i zaimplementowane z klasy Animal
, npmakeEggs
, getBackFromTheFreezedState
i tak dalej. Tak więc w niektórych przypadkach w twojej funkcji możesz chcieć znać konkretny typ, aby wywołać dodatkowe zachowania.
Na przykład, w przypadku, gdy jest poranek, a jeśli jest to tylko zwierzę, wtedy wołasz eat
, w przeciwnym razie, jeśli jest to człowiek, najpierw zadzwoń washHands
, getDressed
a dopiero potem zadzwoń eat
. Jak poradzić sobie z tymi sprawami? Polimorfizm umiera. Musisz znaleźć typ obiektu, który brzmi jak zapach kodu. Czy istnieje wspólne podejście do obsługi tych przypadków?
Eater
interfejs za pomocą tejeat()
metody, to jako klient nie będziesz się przejmować tym, żeHuman
implementacja musi najpierw wywołaćwashHands()
igetDressed()
jest to szczegół implementacji tej klasy. Jeśli jako klient zależy Ci na tym fakcie, najprawdopodobniej nie używasz odpowiedniego narzędzia do tego zadania.getDressed
przed nimieat
, tak nie jest w przypadku obiadu. W zależności od okolicznościwashHands();if !dressed then getDressed();[code to actually eat]
może być najlepszym sposobem na wdrożenie tego dla człowieka. Inną możliwością jest to, czy inne rzeczy tego wymagająwashHands
i / lubgetDressed
są nazywane? Załóżmy, że maszleaveForWork
? Być może będziesz musiał tak ustrukturyzować przepływ programu, aby tak się nazywał na długo wcześniej.Odpowiedzi:
Zależy. Niestety nie ma ogólnego rozwiązania. Pomyśl o swoich wymaganiach i spróbuj dowiedzieć się, co te rzeczy powinny zrobić.
Na przykład powiedziałeś rano, że różne zwierzęta robią różne rzeczy. Co powiesz na wprowadzenie metody
getUp()
lubprepareForDay()
czy coś takiego. Następnie możesz kontynuować polimorfizm i pozwolić każdemu zwierzęciu wykonywać swoją poranną rutynę.Jeśli chcesz rozróżniać zwierzęta, nie powinieneś przechowywać ich bez rozróżnienia na liście.
Jeśli nic innego nie działa, możesz wypróbować Wzorzec Odwiedzającego , który jest swego rodzaju hackiem, umożliwiającym dynamiczne wysyłanie, w którym możesz przesłać gościa, który otrzyma od zwierząt wywołania zwrotne dokładnie takie jak w przypadku danego typu. Chciałbym jednak podkreślić, że powinno to być ostatecznością, jeśli wszystko inne zawiedzie.
źródło
To dobre pytanie i jest to rodzaj kłopotów dla wielu ludzi, którzy próbują zrozumieć, jak korzystać z OO. Myślę, że większość programistów ma z tym problem. Chciałbym móc powiedzieć, że większość mi się to udaje, ale nie jestem pewien, czy tak jest. Z mojego doświadczenia wynika, że większość programistów używa toreb pseudo-OO .
Po pierwsze, wyjaśnię. To nie twoja wina. Sposób, w jaki zwykle naucza się OO, jest wysoce wadliwy.
Animal
Przykładem jest sprawca premier, IMO. Zasadniczo mówimy, porozmawiajmy o przedmiotach, co mogą zrobić.Animal
Możeeat()
a możespeak()
. Wspaniały. Teraz stwórz zwierzęta i napisz, jak jedzą i mówią. Teraz znasz OO, prawda?Problem polega na tym, że zbliża się to do OO ze złego kierunku. Dlaczego w tym programie są zwierzęta i dlaczego muszą mówić i jeść?
Trudno mi myśleć o prawdziwym użyciu danego
Animal
typu. Jestem pewien, że istnieje, ale omówmy coś, co moim zdaniem jest łatwiejsze do uzasadnienia: symulację ruchu. Załóżmy, że chcemy modelować ruch w różnych scenariuszach. Oto kilka podstawowych rzeczy, które musimy mieć, aby móc to zrobić.Możemy zagłębiać się w różnego rodzaju rzeczy dla pieszych i pociągów, ale utrzymamy prostotę.
Zastanówmy się
Vehicle
. Jakie możliwości potrzebuje pojazd? Musi podróżować po drodze. Musi być w stanie zatrzymać się na sygnałach. Musi być w stanie nawigować na skrzyżowaniach.Jest to prawdopodobnie zbyt proste, ale to początek. Teraz. A co ze wszystkimi innymi rzeczami, które może zrobić pojazd? Mogą skręcić z drogi i wpuścić się do rowu. Czy to część symulacji? Nie. Nie potrzebuję tego. Niektóre samochody i autobusy mają układ hydrauliczny, który pozwala im odpowiednio podskakiwać lub klęczeć. Czy to część symulacji? Nie. Nie potrzebuję tego. Większość samochodów pali benzynę. Niektórzy nie. Czy elektrownia jest częścią symulacji? Nie. Nie potrzebuję tego. Rozmiar koła? Nie potrzebuję tego Nawigacja GPS? System audio-nawigacyjny? Nie potrzebuję ich.
Musisz tylko zdefiniować zachowania, których zamierzasz użyć. W tym celu myślę, że często lepiej jest budować interfejsy OO z kodu, który z nimi współdziała. Zaczynasz z pustym interfejsem, a następnie zaczynasz pisać kod, który wywołuje nieistniejące metody. W ten sposób wiesz, jakich metod potrzebujesz w interfejsie. Gdy to zrobisz, zaczniesz definiować klasy, które implementują te zachowania. Zachowania, które nie są używane, są nieistotne i nie trzeba ich definiować.
Chodzi o to, że możesz dodawać nowe implementacje tych interfejsów później bez zmiany kodu wywołującego. Jedynym sposobem, który działa, jest to, że potrzeby kodu wywołującego określają, co dzieje się w interfejsie. Nie ma sposobu, aby zdefiniować wszystkie zachowania wszystkich możliwych rzeczy, które można by wymyślić później.
źródło
TL; DR:
Pomyśl o abstrakcji i metodach, które mają zastosowanie do wszystkich podklas i obejmuj wszystko, czego potrzebujesz.
Najpierw zostańmy z twoim
eat()
przykładem.Jest to właściwość bycia człowiekiem, który jako warunek jedzenia, chce umyć ręce i ubrać się przed jedzeniem. Jeśli chcesz, żeby ktoś przyszedł do ciebie, aby zjeść z tobą śniadanie, nie każesz mu myć rąk i ubierać się, robią to sami, gdy ich zaprosisz, lub odpowiadają: „Nie, nie mogę przyjść ponad nie umyłem rąk i jeszcze nie jestem ubrany ".
Powrót do oprogramowania:
Jako
Human
przykład nie chce jeść bez warunków wstępnych, że mamHuman
„seat()
metody zrobieniawashHands()
igetDressed()
jeśli to nie zostało zrobione. To nie powinno być twoja praca jakoeat()
Wiedzieć o tej osobliwości dzwoniącej. Alternatywą upartego człowieka byłoby rzucenie wyjątku („Nie jestem gotowy do jedzenia!”), Jeśli warunki wstępne nie są spełnione, co powoduje, że jesteś sfrustrowany, ale przynajmniej informowany, że jedzenie nie działa.Co
makeEggs()
?Polecam zmienić twój sposób myślenia. Prawdopodobnie chcesz wykonać zaplanowane poranne obowiązki wszystkich istot. Ponownie, jako dzwoniący, nie powinno być twoim obowiązkiem wiedzieć, jakie są ich obowiązki. Polecam więc
doMorningDuties()
metodę, którą wszystkie klasy implementują.źródło
Odpowiedź jest dość prosta.
Nie musisz sobie z tym poradzić, ponieważ nie miałoby to sensu. Interfejs jest zwykle projektowany w zależności od tego, jak będzie używany. Jeśli twój interfejs nie definiuje mycia rąk, nie obchodzi cię to jako rozmówcy interfejsu; gdybyś to zrobił, zaprojektowałbyś to inaczej.
Na przykład w pseudokodzie:
Teraz wdrażasz
IMorningPerformer
doAnimal
po prostu jedzenia, aHuman
także do mycia rąk i ubierania się. Osoba dzwoniąca z twojej metody MorningTime może mniej obchodzić, czy jest to człowiek czy zwierzę. Wszystko, czego chce, to poranna rutyna, którą każdy obiekt zachwyca dzięki OO.A może to?
Dlaczego to zakładasz? Myślę, że może to być błędne założenie.
Tak, zwykle rozwiązuje się to dzięki starannie zaprojektowanej hierarchii klas lub interfejsów. Zauważ, że w powyższym przykładzie nic nie stoi w sprzeczności z podanym przez ciebie przykładem, jednak prawdopodobnie poczujesz się niezadowolony, ponieważ poczyniłeś pewne założenia, których nie napisałeś w pytaniu od momentu pisania , a te założenia prawdopodobnie zostały naruszone.
Możliwe jest udanie się do króliczej nory przez zaostrzenie twoich założeń i modyfikowanie odpowiedzi, aby nadal je spełniać, ale nie sądzę, by to było przydatne.
Projektowanie dobrych hierarchii klas jest trudne i wymaga dużego wglądu w domenę biznesową. W przypadku złożonych domen można przejść przez dwie, trzy lub nawet więcej iteracji, ponieważ dopracowują one swoje zrozumienie interakcji różnych podmiotów w swojej domenie biznesowej, dopóki nie uzyskają odpowiedniego modelu.
Tam brakuje uproszczonych przykładów zwierząt. Chcemy uczyć w prosty sposób, ale problem, który staramy się rozwiązać, nie jest oczywisty, dopóki nie zagłębisz się w to, co wiąże się z bardziej złożonymi przemyśleniami i domenami.
źródło