Visual Basic 2005 Coach



Dovnload 477.64 Kb.
Pagina9/13
Datum20.08.2016
Grootte477.64 Kb.
1   ...   5   6   7   8   9   10   11   12   13

7.3. Polymorfisme

7.3.1. Introductie

Na het introduceren van bovenstaande concepten wordt het tijd voor het echte OO-werk, met name polymorfisme en overerving (inheritance). Bij overerving komt het erop neer dat een klasse uitgebreid wordt met extra (of gewijzigde) functionaliteit. Concreet ziet dit er als volgt uit, voortgaand op ons voorbeeldje:


Class ModelA

Implements IAuto


'Implementatiespecifieke details
End Class
Class VerbeterdModelA

Inherits ModelA


'Implementatiespecifieke details
End Class
Hierdoor kunnen we op elke plaats waar een ModelA auto gebruikt wordt in code ook een instantie van VerbeterdModelA doorgeven, vermits een VerbeterdModelA-instantie ook een Model A is. Een Inherits-relatie kan dus gezien worden als een “is een”-relatie.
We noemen een klasse die overerft van een andere klasse een subklasse terwijl de klasse waarvan overgeërfd wordt een ouderklasse (parent class) of basisklasse genoemd wordt. In het .NET Framework stamt elke klasse standaard af van System.Object. Onder .NET kan een klasse ten hoogste van één ouderklasse overerven maar kan een klasse wel meerdere interfaces implementeren.

7.3.2. Werking

Laten we een kijkje nemen naar een concreet voorbeeld van een klassenhiërarchie gebouwd met Visual Basic. Om te beginnen dienen we na te gaan hoe we een basisklasse definiëren waarvan overgeërfd zal worden. Hiervoor grijpen we terug naar ons Auto-voorbeeld maar zullen we – om de complexiteit van het voorbeeld te reduceren – het gebruik van interfaces achterwege laten en een zéér eenvoudig voorbeeld beschouwen.


Class ModelA
Overridable Sub Start()

'Start motor

'Start dashboard controlepaneel

'Voer systeemtest uit

End Sub
Sub Noodstop()

'Schakel alle remsystemen in

'Stop motor

End Sub
End Class


Hierin hebben we een methode Start gedefinieerd als Overridable, waardoor subklassen de methode kunnen “verbeteren” (override). Een andere methode Noodstop is echter niet als Overridable gedeclareerd waardoor deze niet door subklassen herschreven kan worden (bijvoorbeeld omdat de methode zeer cruciaal is). Dit ModelA type is dus perfect bruikbaar en functioneel maar laat uitbreiding toe door subklassen te definiëren.
Tijd om te gaan kijken naar een dergelijke subklasse. Veronderstel dat we een nieuw model wensen te bouwen dat direct afstamt van ModelA (d.w.z. alle basisfunctionaliteit blijft dezelfde) maar we een boordcomputer wensen toe te voegen voor geavanceerde functies. In een dergelijk geval moet de methode start aangepast worden zodat de boordcomputer ook opgestart wordt. Daarnaast zullen extra functies nodig zijn om met de boordcomputer te communiceren.
Class VerbeterdModelA

Inherits ModelA


Overrides Sub Start()

MyBase.Start()'Voer alle basisprocedures uit

'Start boordcomputer op

End Sub
Function PatchBoordcomputer(ByRef patch As SoftwarePatch) As Boolean

'Voer patching uit

Return patchGelukt

End Function
End Class
Dit VerbeterdModelA type gedraagt zich volledig als een ModelA type wat betreft de beschikbare functionaliteit maar voegt extra functies toe. Eerst een vooral wordt de methode Start van het oudertype ModelA aangepast via het sleutelwoord Overrides. De oproep naar MyBase.Start() roept de implementatie aan van het bovenliggende type. Daarna wordt extra functionaliteit toegevoegd. De variabele MyBase verwijst steeds naar het rechtstreekse “supertype” van de huidige klasse. Merk op dat elke klasse die niet expliciet overerft van een ander type impliciet overerft van System.Object.

Verder we de functie PatchBoordcomputer toegevoegd aan het type VerbeterdModelA. Het bovenliggende type ModelA ondersteunt dit niet, de functionaliteit is dus totaal nieuw voor het overgeërfde type. Veronderstel nu volgende module:
Class Garagist
Sub ControleerAuto(ByRef auto As ModelA)

auto.Start()

'Controleer of motor draait
'Controleer of systeemtests correct werden uitgevoerd

auto.Noodstop()


'Meet reactietijd en vergelijk met specificaties

End Sub


End Class
Zoals u ziet kan elke garagist die kan omgaan met ModelA wagens ook zonder probleem een VerbeterdModelA wagen controleren. Volgende oproepen zullen perfect werken:
Dim wagen1 As ModelA = ...

garagist.ControleerAuto(wagen1)

Dim wagen2 As VerbeterdModelA = ...

garagist.ControleerAuto(wagen2)


Veronderstel dat twee auto objecten, respectievelijk wagen1 met type ModelA en wagen2 met type VerbeterdModelA, verkregen worden en dan door de controleprocedure gehaald worden. Binnenin die controleprocedure wordt de methode Start opgeroepen. Bij de controle van wagen1 zal de procedure Start van ModelA opgeroepen worden, terwijl bij wagen2 de procedure start van VerbeterdModelA wordt opgeroepen. Het is belangrijk in te zien dat bij compilatie van het programma niet kan bepaald worden welke methode opgeroepen moet worden. Het is pas bij uitvoering van het programma dat de te gebruiken procedure wordt gekozen, op basis van het type van het object. Dit noemen we polymorfisme.
Noot: Eerder vermeldden we reeds dat de Common Language Runtime (CLR) en de Base Class Library (BCL) transparant zijn met betrekking tot de gebruikte programmeertalen dankzij het gebruik van Intermediate Language (IL). Dit heeft zeer verregaande positieve implicaties zoals bijvoorbeeld cross language inheritance. Een type gedefineerd in om het even welke .NET taal kan overgeërfd worden in om het even welke .NET taal, bijvoorbeeld een type Auto geschreven in C# kan overgeërfd worden bij de definitie van Oldtimer in VB.

7.3.3. Een aantal sleutelwoorden

Nu we ons de basisprincipes van overerving en polymorfisme eigen gemaakt hebben, is het tijd om iets verder in te gaan op dit onderwerp.



7.3.3.1. Keuze van een toegangsniveau

Klassen, interfaces, methoden, properties en dergelijke hebben allemaal een notie van toegangsniveaus. Deze bepalen waar de diverse members zichtbaar zijn en gebruikt kunnen worden. In totaal zijn er vijf verschillende toegangsniveaus ondersteund in Visual Basic:




  • Public wordt gebruikt om aan te duiden dat een member overal, in om het even welke andere assembly en vanuit welk type dan ook, zichtbaar is.

  • Protected betekent dat het item enkel zichtbaar is voor subklassen en is dus bruikbaar in de context van klassenhiërarchieën.

  • Private limiteert toegang tot de huidige klasse zelf. Elders is de member niet zichtbaar of toegankelijk.

  • Friend legt de zichtbaarheidsgrens op de huidige assembly. Een assembly kan meerdere types bevatten. Enkel types in dezelfde assembly zien de “friend members”.

  • Protected Friend combineert logischerwijs twee zaken. Aangeduide members zijn enkel beschikbaar binnen dezelfde assembly en voor subklassen.

Binnen een klasse zijn alle members beschikbaar ongeacht hun toegangsniveau. Meer gedetailleerde informatie over toegangsniveaus is te vinden in de MSDN documentatie.



7.3.3.2. Overrides en Overridable

De sleutelwoorden Overrides en Overridable kwamen we reeds eerder tegen. Standaard kunnen members niet door subklasses overridden worden. Het is dus noodzakelijk om members waarvan je wenst toe te staan dat deze door subklasses kunnen uitgebreid of aangepast worden te markeren als Overridable:


Class Ouder
Overridable Sub EenProcedure()

'Implementatie

End Sub
End Class
In subtypes moet dan expliciet aangegeven worden dat u een member van het oudertype wenst aan te passen door vermelding van het sleutelwoord Overrides:
Class Kind

Inherits Ouder


Overrides Sub EenProcedure()

'Implementatie

End Sub
End Class

7.3.3.3. MyBase, MyClass en Me

Ook het sleutelwoord MyBase ontmoetten we reeds in vorige voorbeelden. In een subklasse kan men gebruik maken van dit sleutelwoord om te verwijzen naar members uit het bovenliggende oudertype, wat onder andere handig is bij het uitbreiden van een bestaande procedure of functie. Indien alle functionaliteit van het bovenliggende type dient overgenomen te worden, kan men via MyBase de originele methode oproepen.


Bij MyClass ligt de situatie anders. U bent wellicht vertrouwd met het sleutelwoord Me uit vorige versies van Visual Basic. Via Me kan u een member uit de huidige klasse oproepen. Het is echter zo dat een oproep via Me polymorfisme volgt. Veronderstel het volgende:
Class Ouder
Sub Iets()

'Implementatie

Me.EenProcedure()

End Sub
Overridable Sub EenProcedure()

'Basisimplementatie

End Sub
End Class


Veronderstel nu dat je een subklasse definieert als volgt:
Class Kind

Inherits Ouder


Overrides Sub EenProcedure()

MyBase.EenProcedure()

'Uitgebreide implementatie

End Sub
End Class


dan zal een oproep naar Iets() op een object van type Kind nog steeds werken. Immers, alle methoden van een oudertype worden door overerving beschikbaar in het kindtype. De vraag is nu echter wat de oproep Me.EenProcedure() binnen Iets() tot gevolg zal hebben. Door gebruik te maken van Me zal per definitie polymorfisme gebruikt worden en zal dus EenProcedure() uit Kind opgeroepen worden. Gebruik je echter:
Sub Iets()

'Implementatie

MyClass.EenProcedure()

End Sub
dan zal steeds EenProcedure() uit Ouder opgeroepen worden, ongeacht de situatie.



7.3.3.4. MustInherit en NonInheritable

Eerder hadden we het reeds over abstracte klassen. Via het sleutelwoord MustInherit wordt een klasse als abstract gedefinieerd waardoor ze niet geïnstantieerd kan worden met behulp van New. Enkel subklassen die overerven van de abstracte (basis)klasse en zelf niet als abstract gedefinieerd zijn kunnen geïnstantieerd worden. Via abstracte klassen is het onder andere mogelijk om een klasse te definiëren die in een interface maar partieel implementeert terwijl de rest van de implementatie van de interface overgelaten wordt aan subtypes. In het kader van ons Auto-voorbeeld zou een abstracte basisklasse een autoreeks kunnen voorstellen, bijvoorbeeld BMW 3 of Mercedes A. Het is evenwel niet mogelijk om een instantie te maken van een dergelijke reeks hoewel een deel van de functionaliteit voor alle auto’s in die reeks reeds aanwezig kan zijn. Het is aan de subklassen – die echte auto’s voorstellen die verkocht kunnen worden en waarmee gereden kan worden – om de gaten in te vullen waar nodig zodat de wagen echt bruikbaar wordt. Op die manier kan men een reeks kenmerken en functionaliteiten centraliseren ongeacht het uiteindelijke type en als het ware reeds een half-afgewerkt skelet afleveren voor verdere invulling (bijvoorbeeld door een ander team op een andere locatie).


NonInheritable doet in feite het tegenovergestelde door aan te geven dat een klasse niet verder uitgebreid mag worden door overerving. Een dergelijke klasse noemt men sealed of verzegeld en vormt dus een eindpunt van een klassenhiërarchie. In de Base Class Library van het .NET Framework vindt u talloze voorbeelden van dergelijke klassen en het is aan te raden klassen als sealed te markeren indien u deze niet gebouwd hebt om verdere uitbreiding toe te laten. Standaard zijn klassen echter niet sealed.

7.3.3.5. MustOverride en NotOverridable

De sleutelwoorden MustOverride en NotOverridable vormen het equivalent van de sleutelwoorden uit vorige paragraaf maar nu op het niveau van procedures, functies en properties. Via MustOverride duidt u bijvoorbeeld aan dat enkel de signatuur van een procedure, functie of property wordt opgegeven maar de implementatie achterwege gelaten wordt. Hierdoor creëert u een abstracte klasse – waarvoor u weliswaar nog steeds het sleutelwoord MustInherit op klasseniveau dient te gebruiken – waar een (niet-abstracte) subklasse genoodzaakt is deze “gaten” in te vullen met concrete implementaties. Een voorbeeld ziet er als volgt uit:


MustInherit Class IkBenAbstract
Overridable Sub EenProcedure()

'Implementatie van deze procedure is aanwezig


'en kan door een subklasse uitgebreid worden

End Sub
Function EenFunctie(ByVal a As Integer) As Integer

'Implementatie van deze functie is aanwezig

'maar kan niet door een subklasse uitgebreid worden

End Function
'Implementatie van deze functie is niet aanwezig

MustOverride Function AndereFunctie(ByVal a As Double) As Boolean


End Class
Via NotOverridable maakt u een procedure, functie of property sealed zodat deze niet kan aangepast worden in een subklasse. Standaard zijn procedures, functies en properties reeds sealed zodat u vooral het sleutelwoord Overridable zult gebruiken om een procedure, functie of property als kandidaat voor uitbreiding te markeren.



1   ...   5   6   7   8   9   10   11   12   13


De database wordt beschermd door het auteursrecht ©opleid.info 2017
stuur bericht

    Hoofdpagina