Próbuję zrozumieć, w jaki sposób polimorfizm jest wykorzystywany w prawdziwym projekcie, ale mogę znaleźć tylko klasyczny przykład (lub coś podobnego do niego) posiadania Animal
klasy nadrzędnej z metodą speak()
oraz wielu klas podrzędnych, które zastępują tę metodę, a teraz możesz wywołać metodę speak()
na dowolnym obiekcie potomnym, na przykład:
Animal animal;
animal = dog;
animal.speak();
animal = cat;
animal.speak();
object-oriented
data-structures
polymorphism
Krzysztof
źródło
źródło
if(x is SomeType) DoSomething()
często, warto zastosować polimorfizm. Dla mnie polimorfizm jest decyzją podobną do tego, kiedy stworzyć osobną metodę, jeśli stwierdzę, że kilkakrotnie powtórzyłem kod, zwykle refaktoryzuję go do metody, a jeśli stwierdzę, że alboif object is this type do this
często tworzę kod, może być warte refaktoryzacji i dodania interfejsu lub klasy.Odpowiedzi:
Strumień jest doskonałym przykładem polimorfizmu.
Strumień reprezentuje „sekwencję bajtów, które można odczytać lub zapisać”. Ale ta sekwencja może pochodzić z pliku, pamięci lub wielu rodzajów połączeń sieciowych. Lub może służyć jako dekorator, który otacza istniejący strumień i przekształca bajty w jakiś sposób, na przykład szyfrowanie lub kompresję.
W ten sposób klient korzystający ze Stream nie musi dbać o to, skąd pochodzą bajty. Tylko, że można je czytać po kolei.
Niektórzy twierdzą, że
Stream
jest to zły przykład polimorfizmu, ponieważ definiuje on wiele „funkcji”, których jego implementatory nie obsługują, na przykład strumień sieciowy umożliwiający tylko odczyt lub zapis, ale nie oba jednocześnie. Lub brak poszukiwania. Ale to tylko kwestia złożoności, ponieważStream
można ją podzielić na wiele części, które można zaimplementować niezależnie.źródło
Stream
są moim ulubionym przykładem polimorfizmu. Nawet nie próbuję już uczyć ludzi wadliwego modelu „zwierzę, ssak, pies”,Stream
ale wykonuję lepszą pracę.Typowym przykładem związanym z grami byłaby klasa podstawowa
Entity
, zapewniająca zwykłych członków, takich jakdraw()
lubupdate()
.Dla bardziej czystego zorientowanego na dane przykładu mogłaby istnieć klasa podstawowa
Serializable
zapewniająca wspólnesaveToStream()
iloadFromStream()
.źródło
Istnieją różne rodzaje polimorfizmów, tym, który jest przedmiotem zainteresowania, jest zazwyczaj polimorfizm / dynamiczna wysyłka w czasie wykonywania.
Bardzo wysokim poziomem opisu polimorfizmu środowiska wykonawczego jest to, że wywołanie metody robi różne rzeczy w zależności od typu argumentów środowiska wykonawczego: sam obiekt jest odpowiedzialny za rozstrzygnięcie wywołania metody. Pozwala to na dużą elastyczność.
Jednym z najczęstszych sposobów korzystania z tej elastyczności jest wstrzykiwanie zależności , np. Dzięki czemu mogę przełączać się między różnymi implementacjami lub wstrzykiwać fałszywe obiekty do testowania. Jeśli wiem z góry, że będzie ograniczona liczba możliwych wyborów, mógłbym spróbować zakodować je na stałe za pomocą warunkowych, np .:
To sprawia, że kod jest trudny do naśladowania. Alternatywą jest wprowadzenie interfejsu dla tej operacji foo i napisanie normalnej implementacji i pozornej implementacji tego interfejsu oraz „wstrzyknięcie” pożądanej implementacji w czasie wykonywania. „Wstrzyknięcie zależności” jest skomplikowanym terminem określającym „przekazanie poprawnego obiektu jako argumentu”.
Jako przykład ze świata rzeczywistego pracuję obecnie nad pewnym problemem związanym z uczeniem maszynowym. Mam algorytm, który wymaga modelu predykcyjnego. Ale chcę wypróbować różne algorytmy uczenia maszynowego. Więc zdefiniowałem interfejs. Czego potrzebuję od mojego modelu predykcyjnego? Biorąc pod uwagę próbkę wejściową, prognozę i jej błędy:
Mój algorytm przyjmuje funkcję fabryczną, która trenuje model:
Mam teraz różne implementacje interfejsu modelu i mogę porównać je ze sobą. Jedna z tych implementacji faktycznie bierze dwa inne modele i łączy je w model wzmocniony. Dzięki temu interfejsowi:
Klasycznym przykładem zastosowania polimorfizmu są GUI. W środowisku GUI, takim jak Java AWT / Swing /…, istnieją różne komponenty . Interfejs komponentu / klasa bazowa opisuje działania, takie jak malowanie się na ekranie lub reagowanie na kliknięcia myszą. Wiele komponentów to kontenery, które zarządzają podkomponentami. Jak taki pojemnik może się rysować?
W tym przypadku kontener nie musi z góry wiedzieć o dokładnych typach podskładników - o ile są one zgodne z
Component
interfejsem, kontener może po prostu wywołaćpaint()
metodę polimorficzną . Daje mi to swobodę rozszerzania hierarchii klas AWT o dowolne nowe komponenty.Istnieje wiele powtarzających się problemów podczas tworzenia oprogramowania, które można rozwiązać, stosując polimorfizm jako technikę. Te powtarzające się pary problem-rozwiązanie nazywane są wzorcami projektowymi , a niektóre z nich są zebrane w księdze o tej samej nazwie. Zgodnie z tą książką, mój model uczenia maszynowego z wtryskiem byłby strategią , której używam do „zdefiniowania rodziny algorytmów, kapsułkowania każdego z nich i uczynienia ich wymiennymi”. Przykład Java-AWT, w którym komponent może zawierać podskładniki, jest przykładem kompozytu .
Ale nie każdy projekt musi wykorzystywać polimorfizm (poza umożliwieniem wstrzykiwania zależności do testów jednostkowych, co jest naprawdę dobrym przypadkiem użycia). Większość problemów jest poza tym bardzo statyczna. W rezultacie klasy i metody często nie są używane do polimorfizmu, ale po prostu jako wygodne przestrzenie nazw i ładna składnia wywołań metod. Np. Wielu programistów woli wywołania metod,
account.getBalance()
niż w większości równoważne wywołania funkcjiAccount_getBalance(account)
. To bardzo dobre podejście, tyle, że wiele wywołań „metod” nie ma nic wspólnego z polimorfizmem.źródło
W większości zestawów narzędzi interfejsu użytkownika widać wiele elementów dziedziczenia i polimorfizmu.
Na przykład w zestawie narzędzi interfejsu użytkownika JavaFX
Button
dziedziczy, zButtonBase
którego dziedziczy, zLabeled
którego dziedziczy, zControl
którego dziedziczy, zRegion
którego dziedziczy, zParent
którego dziedziczy, zNode
którego dziedziczyObject
. Wiele warstw zastępuje niektóre metody z poprzednich.Jeśli chcesz, aby ten przycisk pojawił się na ekranie, dodaj go do
Pane
, który może zaakceptować wszystko, co odziedziczy poNode
nim. Ale skąd okienko wie, co zrobić z przyciskiem, gdy po prostu widzi go jako ogólny obiekt węzła? Tym obiektem może być wszystko. Panel może to zrobić, ponieważ przycisk na nowo definiuje metody węzła za pomocą dowolnej logiki specyficznej dla przycisku. Panel po prostu wywołuje metody zdefiniowane w węźle i pozostawia resztę samemu obiektowi. To doskonały przykład zastosowanego polimorfizmu.Zestawy narzędzi interfejsu użytkownika mają bardzo duże znaczenie w świecie rzeczywistym, dzięki czemu są przydatne do nauczania zarówno ze względów naukowych, jak i praktycznych.
Jednak zestawy narzędzi interfejsu użytkownika mają również znaczną wadę: są one zwykle ogromne . Kiedy inżynier oprogramowania neofita próbuje zrozumieć wewnętrzne funkcjonowanie wspólnego frameworku interfejsu użytkownika, często napotyka ponad sto klas , z których większość służy bardzo ezoterycznym celom. „Co do cholery jest
ReadOnlyJavaBeanLongPropertyBuilder
? Czy to ważne? Czy muszę mieć , aby zrozumieć, co to jest dobre dla?” Początkujący mogą łatwo zgubić się w tej nory królika. Mogą więc albo uciekać w przerażeniu, albo pozostać na powierzchni, gdzie tylko uczą się składni i starają się nie myśleć zbyt intensywnie o tym, co naprawdę dzieje się pod maską.źródło
Chociaż istnieją już ładne przykłady, innym jest zastąpienie zwierząt urządzeniami:
Device
może byćpowerOn()
,powerOff()
,setSleep()
i puszkigetSerialNumber()
.SensorDevice
Można zrobić to wszystko i zapewniają polimorficzne funkcje, takie jakgetMeasuredDimension()
,getMeasure()
,alertAt(threashhold)
iautoTest()
.getMeasure()
nie będą realizowane w ten sam sposób dla czujnika temperatury, detektora światła, detektora dźwięku lub czujnika wolumetrycznego. I oczywiście każdy z tych bardziej wyspecjalizowanych czujników może mieć dostępne dodatkowe funkcje.źródło
Prezentacja jest bardzo popularną aplikacją, być może najczęstszą jest ToString (). Który jest w zasadzie Animal.Speak (): Mówisz obiektowi, aby się zamanifestował.
Mówiąc bardziej ogólnie, mówisz przedmiotowi, aby „zrobił to, co on”. Pomyśl Zapisz, Załaduj, Zainicjuj, Usuń, ProcessData, GetStatus.
źródło
Moim pierwszym praktycznym zastosowaniem polimorfizmu była implementacja Heap w java.
Miałem klasę podstawową z implementacją metod insert, removeTop, gdzie różnica między maksymalnym i minimalnym stosem byłaby tylko sposobem porównania metod.
Więc kiedy chciałem mieć MaxHeap i MinHeap, mogłem po prostu użyć dziedziczenia.
źródło
Oto scenariusz z życia polimorfizmu aplikacji sieci Web / bazy danych :
Używam Ruby on Rails do tworzenia aplikacji internetowych, a jedną z cech wspólnych wielu moich projektów jest możliwość przesyłania plików (zdjęć, plików PDF itp.). Na przykład,
User
może mieć wiele zdjęć profilowych, a takżeProduct
może mieć wiele zdjęć produktów. Oba mają działanie przesyłania i przechowywania obrazów, a także zmiany rozmiaru, generowania miniaturek itp. Aby pozostać SUCHYM i dzielić się tym zachowaniemPicture
, chcemy uczynićPicture
polimorficzny, aby mógł należeć zarówno do, jakUser
i doProduct
.W Railsach zaprojektowałbym moje modele jako takie:
oraz migracja bazy danych, aby utworzyć
pictures
tabelę:Kolumny
imageable_id
iimageable_type
są używane wewnętrznie przez Railsy. Zasadniczo,imageable_type
posiada nazwę klasy ("User"
,"Product"
etc.), iimageable_id
jest id powiązanego rekordu. Takimageable_type = "User"
iimageable_id = 1
będzie rekord wusers
tabeli zid = 1
.To pozwala nam robić rzeczy, takie jak
user.pictures
uzyskiwanie dostępu do zdjęć użytkownika, a takżeproduct.pictures
uzyskiwanie zdjęć produktu. Następnie wszystkie zachowania związane z obrazem są zawarte wPhoto
klasie (a nie w osobnej klasie dla każdego modelu, który potrzebuje zdjęć), więc rzeczy pozostają SUCHE.Więcej informacji: skojarzenia polimorficzne w szynach .
źródło
Dostępnych jest wiele algorytmów sortowania, takich jak sortowanie bąbelkowe, sortowanie za pomocą wstawiania, sortowanie szybkie, sortowanie sterty itp. Mają one różną złożoność, a wybór jednego z nich jest optymalny i zależy od różnych czynników (np. Rozmiaru tablicy)
Klient wyposażony w interfejs sortowania dotyczy tylko dostarczenia tablicy jako danych wejściowych, a następnie otrzymania posortowanej tablicy. Podczas działania w zależności od pewnych czynników można zastosować odpowiednią implementację sortowania. Jest to jeden z prawdziwych przykładów użycia polimorfizmu.
To, co opisałem powyżej, jest przykładem polimorfizmu środowiska uruchomieniowego, podczas gdy przeciążenie metody jest przykładem polimorfizmu w czasie kompilacji, w którym jest on zgodny w zależności od typów parametrów i / p i o / p oraz liczby parametrów wiążących osobę wywołującą odpowiednią metodę w samym czasie zgodności.
Mam nadzieję, że to wyjaśnia.
źródło