Tijdens het leren object-georiënteerd programmeren, en na het beheersen van de basis van objecten, velden en methodes, besteedt men de meeste tijd aan overerving. Overerving betekent dat we een deel van de implementatie overnemen van een basisklasse. Je moet gewoon een subklasse van een basisklasse maken om elk niet-privaat veld en methode te erven.
Een auto en een vliegtuig zijn voertuigen dus het is duidelijk dat beide klassen moeten worden uitgebreid vanuit hun gemeenschappelijke basisklasse genaamd Voertuig. Dit is een typisch academisch voorbeeld, maar terwijl we beslissen om deze klassen te binden met de overervingsrelatie, moeten we ons bewust zijn van enkele gevolgen.
Fig. 1 Implementatie van overervingsrelatie.
In dit geval zijn de klassen nauw met elkaar verbonden - dit betekent dat wijzigingen in het gedrag van elke klasse kunnen worden bereikt door wijzigingen aan te brengen in de basisklasse code. Dit kan zowel een voordeel als een nadeel zijn - het hangt af van wat voor gedrag we verwachten. Als de overerving op het verkeerde moment wordt toegepast, kan het proces van het toevoegen van een nieuwe functie op implementatieproblemen stuiten omdat het niet past in het gecreëerde klassenmodel. We zullen moeten kiezen tussen het dupliceren van de code of het reorganiseren van ons model - en dat kan een erg tijdrovend proces zijn. We kunnen de code die de overervingsrelatie uitvoert "open-gesloten" noemen - dit betekent dat het open is voor uitbreidingen maar gesloten voor wijzigingen. Ervan uitgaande dat er in de voertuigklasse een algemene, gedefinieerde motorwerking is voor elk voertuig, op het moment dat we een voertuig zonder motor (bijvoorbeeld een fiets) willen toevoegen aan onze klassenhiërarchie, moeten we een aantal serieuze wijzigingen aanbrengen in onze klassen.
Klasse Voertuig
def start_motor
einde
def stop_motor
einde
einde
Klasse Vliegtuig < Voertuig
def bewegen
start_motor
...
stop_motor
einde
einde
Samenstelling
Als we slechts geïnteresseerd zijn in een deel van het gedrag van de bestaande klasse, is een goed alternatief voor overerving het gebruik van compositie. In plaats van subklassen te maken die elk gedrag erven (degene die we nodig hebben en degene die we helemaal niet nodig hebben), kunnen we de functies die we nodig hebben isoleren en onze objecten uitrusten met verwijzingen daarnaar. Op deze manier geven we de gedachte op dat object is een soort van een basisobject, ten gunste van de bewering dat het bevat slechts enkele delen van zijn eigenschappen.
Fig. 2 De samenstelling gebruiken
Door deze aanpak te volgen kunnen we de code die verantwoordelijk is voor de werking van de motor isoleren naar de autonome klasse genaamd Engine en er alleen naar verwijzen in de klassen die voertuigen met motoren representeren. Het isoleren van de functies met het gebruik van compositie zal de structuur van de voertuigklasse eenvoudiger maken en de inkapseling van individuele klassen versterken. Nu is de enige manier waarop voertuigen een effect kunnen hebben op de motor het gebruik van de openbare interface, omdat ze geen informatie meer hebben over de implementatie ervan. Bovendien is het mogelijk om verschillende typen motoren in verschillende voertuigen te gebruiken en zelfs om ze uit te wisselen terwijl het programma draait. Natuurlijk is het gebruik van compositie niet foutloos - we creëren een losjes verbonden klassenset, die gemakkelijk kan worden uitgebreid en open staat voor wijzigingen. Maar tegelijkertijd is elke klasse verbonden met vele andere klassen en moet informatie hebben over hun interfaces.
klasse Voertuig
einde
klasse Motor
def start
end
def stop
einde
einde
Klasse Vliegtuig < Voertuig
def initialiseren
@engine = Engine.new
einde
def bewegen
@engine.start
@engine.stop
einde
def verander_motor(nieuwe_motor)
@engine = nieuwe_engine
einde
einde
De keuze
Beide beschreven benaderingen hebben voor- en nadelen, dus hoe kies je ertussen? Inheritance is een specialisatie, dus het is het beste om ze alleen toe te passen voor problemen waarin er "is-a" type relaties zijn - zodat we te maken hebben met de echte hiërarchie van types. Omdat overerving klassen nauw met elkaar verbindt, moeten we ten eerste altijd overwegen of we compositie moeten gebruiken of niet. Compositie moet worden toegepast bij problemen waarbij er "has-a" type relaties zijn - dus de klasse heeft veel onderdelen, maar het is iets meer dan een verzameling klassen. Een vliegtuig bestaat uit onderdelen maar op zichzelf is het iets meer - het heeft extra mogelijkheden, zoals vliegen. Als we verder gaan met dit voorbeeld, kunnen individuele onderdelen voorkomen in verschillende specialistische varianten, en dan is het een goed moment om overerving te gebruiken.
Zowel inheritance als composition zijn slechts hulpmiddelen die programmeurs tot hun beschikking hebben, dus het kiezen van het juiste hulpmiddel voor een bepaald probleem vereist ervaring. Dus laten we oefenen en leren van onze fouten 🙂