– Cześć, Amigo! W końcu dotarliśmy do czegoś naprawdę ciekawego! Dzisiaj opowiem Ci o dziedziczeniu wielokrotnym. Jest ono naprawdę fascynującym i potężnym narzędziem. I gdyby nie pewne pojawiające się problemy, to Java także obsługiwałaby wielokrotne dziedziczenie klas. Musimy się jednak zadowolić wielokrotnym dziedziczeniem interfejsów. To też jest całkiem fajne.

Dziedziczenie wielu interfejsów - 1

Wyobraź sobie, że piszesz grę komputerową. Jej bohaterowie (Twoje obiekty) muszą się zachowywać na wiele bardzo konkretnych sposobów: poruszać się po świecie gry, zbierać przedmioty, przechodzić misje, zabijać przeciwników, porozumiewać się czy ratować inne postacie. Powiedzmy, że podzieliłeś wszystkie obiekty na 20 kategorii. To oznacza, że jeśli Ci się poszczęści, definiując swoje obiekty, uda ci się zamknąć w 20 klasach. Jest jednak pewien haczyk: pomyśl, ile unikalnych form interakcji będą miały te obiekty? Każdy typ obiektu może wchodzić w unikalne interakcje z 20 innymi typami obiektów (liczymy także interakcje z obiektami tego samego typu). Innymi słowy, potrzebujesz napisać kod dla 20 x 20 = 400 interakcji! A jeśli liczba unikalnych obiektów wzrośnie z 20 do 100, to liczba interakcji wyniesie 10000!

– Wow! Teraz już rozumiem, dlaczego programowanie jest takie trudne.

– Wręcz przeciwnie – jest ono całkiem proste. Dzieje się tak ze względu na wiele przydatnych abstrakcji. Oraz dzięki wielokrotnemu dziedziczeniu interfejsów.

Często możemy uprościć interakcje danych obiektów, jeśli w te interakcje wchodzą raczej role i/lub możliwości niż same te obiekty. Jak już wiemy, możliwości można łatwo dodawać do klasy, kiedy implementuje ona dany interfejs.

Kiedy pisze się duży program, programiści zazwyczaj zaczynają pracę od:

1) Zidentyfikowania możliwości/funkcji.

2) Zdefiniowania interakcji między tymi funkcjami.

3) Przypisania funkcji do wszystkich klas.

– Może dasz jakiś przykład?

– Oczywiście. Przyjrzyjmy się funkcjom w kreskówce «Tom i Jerry».

Kod Java Opis
interface Moveable
{}
— Funkcja/możliwość poruszania się.
interface Eatable
{}
— Funkcja/możliwość bycia zjedzonym.
interface Eat
{}
— Funkcja/możliwość zjedzenia kogoś.
class Tom extends Cat implements Moveable, Eatable, Eat
{}
Tom jest kotem, który charakteryzuje się trzema funkcjonalnościami:
1) Może się poruszać
2) Może kogoś zjeść
3) Może zostać przez kogoś (np. psa) zjedzonym
class Jerry extends Mouse implements Moveable, Eatable
{}
Jerry jest myszą, która posiada dwie funkcjonalności:
1) Może się poruszać
2) Może zostać przez kogoś zjedzona
class Killer extends Dog implements Moveable, Eat
{}
Killer jest psem, który posiada dwie funkcjonalności: 1) Może się poruszać 2) Może kogoś zjeść

Znając tylko te trzy funkcjonalności (interfejsy), możesz napisać program i opisać prawidłowe interakcje pomiędzy tymi funkcjonalnościami. Na przykład, obiekt będzie gonił (za pośrednictwem interfejsu Moveable) «tego, kogo może zjeść» i uciekał przed «tym, kto może zjeść jego». A wszystko to bez wiedzy o konkretnych obiektach. Jeśli dodasz więcej obiektów (klas) do programu i utrzymasz te funkcjonalności, wciąż będzie on świetnie działał, właściwie kontrolując zachowanie Twoich obiektów.