Polymorfisme (informatica)
Polymorfisme staat voor veelvormigheid. In het geval van de informatica wordt hiermee bedoeld het gelijkvormig zijn van de interface van klassen en objecten, maar met verschillende implementaties. De gelijkvormigheid betreft dan voornamelijk het gebruik en de naamgeving van operaties (of methodes). Dit wordt gezien als een zeer belangrijke factor om het objectgeöriënteerd programmeren goed tot zijn recht te laten komen, maar in feite behoort elke programmatuur dit toe te passen.
Inhoud |
[bewerk] Voorbeelden
[bewerk] Plus operator
Een zeer bekend voorbeeld van polymorfisme in programmeertalen is het gebruik van de plus-operator om getallen op te tellen. Als in een fictieve programmeertaal de volgende code verschijnt
int a a = 1 + 2
dan wordt de plus door de compiler vertaald naar een ADD-instructie op machinetaalniveau.
Als in dezelfde fictieve taal de volgende code verschijnt
float a a = 1.0 + 2.0
dan wordt het plusteken door de compiler naar heel andere code vertaald, namelijk naar de code voor het optellen van floating point getallen. De plus-operator betekent in deze gevallen technisch dus iets heel anders, maar logisch gezien doen ze hetzelfde, namelijk optellen. Dat soort gelijkvormigheid is de essentie van polymorfisme.
Het gebruik van de plus-operator in Java om strings aan elkaar te plakken is in dit licht niet correct. Ook de unaire plus en min om het teken van een getal aan te geven (bijvoorbeeld -1) is niet polymorf. Beide voorbeelden zijn voor de argeloze lezer echter meteen duidelijk. Dit toont aan dat polymorfisme, alhoewel zeer belangrijk, niet in alle gevallen zaligmakend is.
De techniek van het gebruik van een operatie (in dit geval de plus) met argumenten van verschillende types en aantallen heet overloading. Alle moderne programmeertalen kennen dit.
[bewerk] Grafische interface
Polymorfisme kan in combinatie met overerving ook worden gebruikt voor het omzeilen van de strenge controle in sommige talen op het type van gebruikte variabelen. Zo was het in standaard Pascal onmogelijk een lijst te definiëren waarvan de onderdelen niet allemaal hetzelfde type hebben. Een voorbeeld daarvan is een lijst controls in een dialoogbox. Wanneer een dialoogbox op het scherm wordt gezet moeten alle controls zichzelf afbeelden. Deze opdracht krijgen die controls van het dialoogboxobject dat daarvoor hun draw-methode aanroept. De programmeur van de dialoogboxklasse hoeft geen rekening te houden met welke controls er allemaal zijn en dezelfde code werkt voor dialoogboxen met zeer verschillende controls.
[bewerk] Overerving
Binnen het objectgeöriënteerd programmeren wordt polymorfisme vaak met overerving in verband gebracht. Overerving is, net als overloading, een manier om polymorfisme te bevorderen. Stel u heeft een basisklasse Dier gemaakt met daarin een operatie AantalPoten() om het aantal poten op te vragen. Alle klassen die afgeleid zijn van deze klasse (bijvoorbeeld de klasse Hond) zal automatisch dezelfde naam gebruiken voor het aantal poten.
[bewerk] Naamconventies
Met overerving alleen kom je er niet. Om polymorfisme te bereiken moet de naamgeving consequent zijn, ook op plekken waar geen sprake is van overerving. Als bijvoorbeeld een (basis)klasse Meubelstuk gedefinieerd wordt moet ook hier de naam AantalPoten() gebruikt worden om het aantal poten op te vragen. Hievoor is het niet nodig een OO-programmeertaal te gebruiken.
Dit kan alleen bereikt worden als de naamgeving van klassen en vooral van operaties vastgelegd wordt. Hierboven is steeds de naam AantalPoten() gebruikt, maar in het afsprakendocument kan het ook anders beschreven staan. Een aantal mogelijkheden zijn bijvoorbeeld: Een operatienaam ...
- ... begint met een kleine letter. aantalPoten()
- ... begint met get als het een leesoperatie is. getAantalPoten()
- ... bestaat uit alleen een zelfstandig naamwoord. Poten()
- ... gebruikt underscore als scheidingsteken tussen woorden. aantal_poten()
Bij programmeertalen waarin de naam slechts een beperkt aantal tekens lang mag zijn is de naam vaak erg cryptisch voor mensen die de naamconventies niet kennen. Hier is het gebruik van naamconventies nog belangrijker.
[bewerk] Goed toepassen
Dit impliceert dat polymorfisme ook verkeerd toegepast kan worden. Het kan op minstens twee manieren misgaan.
- Polymorfisme moet niet alleen op de naam toegepast worden, maar ook op de argumenten (of parameters) en het returntype van de operatie. Stel, wel hebben een operatie setAantalPoten() met als argument een integer die het aantal poten bevat. Dit is logisch, en iedereen zal dit op de gok direct goed gebruiken. Maar in ingewikkeldere gevallen is dit een stuk lastiger. Het is niet zo eenvoudig om hier regels voor te maken zoals in het geval van de naamgeving. Het gebruik van de argumenten dient echter wel consequent te zijn.
- Dezelfde naamgeving kan gebruikt worden terwijl dit niet terecht is. Bijvoorbeeld de operatie AantalPoten() wordt toegepast op een bedrijf om het aantal onderdelen waaruit het bedrijf bestaat aan te geven. Dit is misleidend. In dit geval kan beter de meest correcte naam toegepast worden, AantalOnderdelen().
[bewerk] Het doel van polymorfisme
De doelen van polymorfisme zijn het leesbaar maken van broncode en het voorkomen dat de programmeur steeds documentatie moet naslaan voor het vinden van de juiste identifier.
Door het gebruik van consequente naamgeving en argumenten is het voor de lezer van de broncode mogelijk deze te begrijpen zonder dat bij elke aanroep documentatie geraadpleegd hoeft te worden, of nog erger, dat de code iets anders doet dan deze suggereert.
Een vuistregel is dat de programmeur door het gokken van de naam een trefkans van 90% heeft. Alleen in die laatste 10% moet de programmeur opnieuw gokken of de documentatie naslaan. Ook voor de argumenten van een operatie geldt een dergelijk percentage.