Multiple dispatch

Sven-Olof Nyström
OOP med Java våren -25
Informationsteknologi
Uppsala Universitet

Enkel dispatch

Normalt gäller:

mottagaren av ett meddelande avgör hur det tolkas

Exempel:

Vid ett metodanrop x.draw() kan en cirkel eller en kvadrat ritas upp, beroende på vilken klass x tillhör.

Multiple dispatch

Men om vi vill välja metod beroende på två objekts klasstillhörighet?

Exempel: En metod overlap(x,y) där x och y kan vara olika figurer (cirklar, kvadrater, trianglar, etc)

När vi kollar om två figurer överlappar använder vi olika lösningar beroende på typ av figur.

En tidigare inlämningsuppgift i kursen (Pasture) hade samma problem. Uppgiften simulerade får, vargar och gräs. En varg kunde äta ett får (men inte gräs eller andra vargar); ett får kunde äta gräs (men inte vargar eller andra får). En varg och ett får kunde befinna sig på samma plats men två vargar eller två får kunde inte det. För att lösa uppgiften måste vi implementera metoder canEat() och isCompatible() som beror av klasstillhörighet hos två objekt.

I alla dessa exempel tillhör båda objekten samma klasshierarki, men man kan förstås tänka sig att objekten tillhör helt orelaterade klasser, till exempel:

present(Figure f, Device d)

Metoden visar/skriver ut/ritar upp en figur på (exempelvis) en skrivare, en plotter eller en bildskärm. Metoden för presentation är olika beroende på typen av hårdvara och på typen av figur.

Testa på typ

En lösning är att man låter det ena objektet testa det andra objektets typ, lämpligtvis med operatorn instanceof.

En nackdel med en sådan lösning är att koden i klassen som gör valet blir beroende av klasshierarkin för det andra objektet. Det som borde ha varit en implementationsdetalj (hur en klass ärver av en annan) påverkar kontrollstrukturen.

Lösning med polymorfi

Först en allmän formulering av problemet:

Vi vill definera en metod q.m(p) där

Vi har olika beteenden för samtliga kombinationer av q och p.

Lösning: Inför metoder mA(), mB(), mC()

I klassen A, låt metoden m(p) anropa p.mA(this), i klassen B anropar m(p) metoden p.mB(this) och så vidare.

I klassen X definierar vi metoderna mA(...), mB(...), mC(...)

Varje beteende definieras i en egen metod.

Så om vi har p av klassen X och q av klassen C och ett anrop q.m(p), anropar m i q metoden p.mA(q). I klassen X i metoden mA() har vi bestämt hur kombinationen av klasserna X och A ska hanteras.

(I Pasture kan man definiera metoder eatenByWolf() och eatenBySheep() för olika typer av varelser. Anropet thing.eatenByWolf() ska förstås ge true om thing är ett får och false annars.)

Fördelar:

Man måste förstås fortfarande bestämma hur olika kombinationer ska hanteras.