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 .:
void foo() {
if (isTesting) {
... // do mock stuff
} else {
... // do normal stuff
}
}
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:
interface Model {
def predict(sample) -> (prediction: float, std: float);
}
Mój algorytm przyjmuje funkcję fabryczną, która trenuje model:
def my_algorithm(..., train_model: (observations) -> Model, ...) {
...
Model model = train_model(observations);
...
y, std = model.predict(x)
...
}
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:
- mój algorytm nie musi wcześniej wiedzieć o konkretnych modelach,
- Mogę łatwo wymieniać modele i
- Mam dużą elastyczność we wdrażaniu moich modeli.
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ć?
void paint(Graphics g) {
super.paint(g);
for (Component child : this.subComponents)
child.paint(g);
}
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 funkcji Account_getBalance(account)
. To bardzo dobre podejście, tyle, że wiele wywołań „metod” nie ma nic wspólnego z polimorfizmem.