Overerving
Overerving verband tussen objecten in OOP -> object kan een ander object gebruiken via berichten, dankzij goede toepassing van inkapseling => goed gedefinieerde opzichzelfstaande objecten. -> overerving : definitie van nieuwe klasse wordt gebaseerd op een bestaande klasse => nieuwe klasse erft alle eigenschappen en gedragingen van bestaande klasse
Overerving => alle methoden en eigenschappen uit de bestaande klasse zitten automatisch in de interface van de nieuwe klasse bestaan voorbeeld: de overerving van Employee
Overerving UML-voorstelling van de klasse Employee
Overerving
Overerving
Overerving
Overerving -> gebruik in loonlijsten bijvoorbeeld -> nu werknemer modelleren die op basis van commissie werkt: CommissionWorker * heeft een basisloon * plus een commissie per verkoop -> afgezien van deze vereiste is CommissionWorker precies hetzelfde als een Employee -> CommissionWorker “is een” Employee.
Overerving Oplossingen: -> met inkapseling: * code uit Employee herhalen * nieuwe code voor commissie en berekenen loon toevoegen * 2x zelfde basiscode : bij fout op 2 plaatsen wijzigingen doorvoeren * niet aan te raden
Overerving -> werknemervariabele in de klasse CommissionWorker opnemen : * alle berichten zoals getLastName( ) en getFirstName( ) aan de instantie van Employee delegeren = proces waarbij een object een bericht aan een ander object doorgeeft om aan een aanvraag te voldoen * herdefinitie van alle methoden in Employee is nodig om alle berichten te kunnen doorgeven => geen optie
Overerving -> overerving:
Overerving
Overerving
Overerving
Overerving *super: verwijzing naar basisklasse : in casu klasse Employee *CommissionWorker erft van Employee : toString( ), getFirstName ( ), getLastName ( ), setFirstName ( ), setLastName ( ), firstName en lastName maken deel uit van de definitie. => publieke interface van Employee wordt een onderdeel van interface van CommissionWorker
Overerving =>elk bericht naar Employee kan ook naar CommissionWorker gestuurd worden dit gebeurt in de volgende main( ) met als resultaat: First Name: Mr Last Name: Sales Total Pay: $10.5
Overerving
Overerving Doel van overerving: -> overerving omvat meer dan alleen gewoon het erven van een publieke interface en een implementatie : dus hergebruik -> herdefinitie van elk gedrag dat u niet bevalt: bijvoorbeeld: toString( ) => software aanpassen als eisen veranderen: * maak nieuwe klasse die oude functionaliteit erft
Overerving * vervang functionaliteit die gewijzigd moet worden -> overriding * of voeg de ontbrekende functionaliteit toe -> voordeel van overriding: werkwijze van een object kan veranderd worden zonder de originele klassedefinitie te veranderen => goed geteste, gevalideerde basiscode kan intact gelaten worden. Zelfs mogelijk als originele broncode van een klasse niet beschikbaar is
Overerving: -> ander belangrijk gebruiksdoel: * een klasse groepeert gerelateerde objecten * Overerving maakt het mogelijk gerelateerde klassen te groeperen en te classificeren wanneer overerving gebruiken voor hergebruik: -> “is een”-regel: een CommissionWorker is een Employee
Overerving: -> mislukt deze regel gebruik dan “bevat een”- regel: beschrijft de relatie waarbij de ene klasse een instantie van een andere klasse bevat -> aggregatie overerving niet enkel voor hebzuchtig hergebruik overervingsrelaties moeten zinvol zijn overervingshiërarchie -> voorstelling door middel van boomstructuur -> = verbindingsdiagram : toont de relaties tussen klassen als gevolg van overerving
Overerving: Eenvoudigste overervingshiërarchie -> child(kind)-parent(ouder)-relatie -> start van elke overervingshiërarchie -> UML-voorstelling : pijl in de zin van de parent -> vb:
Overerving: Child-klasse = subklasse erft rechtstreeks van parent- klasse = superklasse overerving = “is een”-relatie =>subklasse erft alle eigenschappen en gedragingen van superklasse, eventueel zelf geërfd van andere klassen => alles wat u met parent kunt doen, kunt u ook met child => dan pas is overervingshiërarchie zinvol ; daarop controleert “is een”- test
Overerving: Child-klasse -> alleen functionaliteit uitbreiden en toevoegen -> nooit functionaliteit verwijderen => als dit nodig is => child voor parent in overervingshiërarchie Parent-klassen en children-klassen lijken op elkaar als echte ouders en kinderen. Klassen delen type-informatie, in plaats van genen.
Overerving: echter meestal: child -> 1 parent ( in Java) wanneer meer dan één parent => meervoudige overerving (of multiple inheritance) (niet in Java) Child-klassen : gedragingen wijzigen: 2 manieren:
Overerving: 2 manieren: 1. nieuwe methodes en eigenschappen toevoegen bijv: kind : piano spelen en ouder niet 2. Herdefiniëren van geërfd gedrag door een oude methode te herschrijven bijv: ouders slecht in wiskunde; kind niet door extra te studeren
Mechanisme van overerving: Een via overerving geconstrueerde klasse erft -> implementatie -> gedragingen -> eigenschappen(attributen) van parent => alle methoden en attributen in interface van parent => ook in interface van child interface parent interface child
Mechanisme van overerving: Een via overerving geconstrueerde klasse kan 3 belangrijke soorten methoden en attributen hebben: Vervangen - De nieuwe klasse erft de methode of ? attribuut ? van de parent, maar levert een nieuwe definitie. Nieuw - De nieuwe klasse voegt een geheel nieuwe methode of attribuut toe. Recursief - De nieuwe klasse erft simpelweg een methode of attribuut van de parent.
Mechanisme van overerving:
Mechanisme van overerving:
Mechanisme van overerving:
Mechanisme van overerving:
Mechanisme van overerving:
Mechanisme van overerving:
Mechanisme van overerving:
Mechanisme van overerving: Alle soorten methoden worden gedemonstreerd: Vervangen methode: toString( ) Nieuwe methoden: getZCoordinate( ); setZCoordinate( ) attribuut: z_coord Recursieve methoden: getXCoordinate( ); setXCoordinate( ) getYCoordinate( ); setYCoordinate( ) attribuut: x_coord; y_coord
Mechanisme van overerving: Vervangen = overriding; = herdefiniëren van een methode =child neemt een methode uit parent en herschrijft deze om het gedrag van de methode te wijzigen. ThreeDimensionalPoint herdefinieert de methode toString ( ) uit TwoDimensionalPoint toString ( ) in TwoDimensionalPoint : -> identificeert de instantie als een tweedimen- sionaal punt en drukt de twee onderdelen af waaruit de coördinaat bestaat
Mechanisme van overerving: Vervangen toString ( ) in ThreeDimensionalPoint : -> identificeert de instantie als een driedimensionaal punt en drukt de 3 onderdelen af waaruit de coördinaat bestaat beschouw volgende main:
Mechanisme van overerving: Vervangen toString ( ) in ThreeDimensionalPoint : -> identificeert de instantie als een driedimensionale punt en drukt de 3 onderdelen af waaruit de coördinaat bestaat beschouw volgende main:
Mechanisme van overerving: Vervangen resultaat van de main: I am a 2 dimensional point. My x coordinate is: 1 My y coordinate is: 2 I am a 3 dimensional point. My z coordinate is: 3
Mechanisme van overerving: Vervangen Maar hoe weet het object nu welke definitie het moet gebruiken? -> antwoord = afhankelijk van onderliggende OO- implementatie, nl. OO-systemen zoeken: 1. definitie in object waar bericht is aan door- gegeven 2. daar -> geen definitie => hoger in hiërarchie kijken tot een definitie wordt gevonden.
Mechanisme van overerving: Vervangen -> dit is de manier waarop een bericht wordt verwerkt en dit is de reden waarom overerving werkt => 1. wordt definitie van de child aangeroepen omdat dit de eerste definitie is die wordt gevonden 2. daar -> geen definitie => hoger in hiërarchie zoeken tot een definitie wordt gevonden.
Mechanisme van overerving: Vervangen
Mechanisme van overerving: Vervangen niet alle methoden en attributen kunnen door child vervangen worden -> afhankelijk van toegangscontrole adhv sleutelwoorden 1. Privé : toegang is beperkt tot de klasse zelf 2. Protected - Beveiligd: toegang is beperkt tot de klasse en children daarvan 3. Public - Publiek: toegang wordt toegestaan aan iedereen
Mechanisme van overerving: Vervangen Beveiligde -> enkel toegang voor klasse en voor subklassen niet publiek maken: alleen zij die een uitgebreide kennis van de klasse hebben mogen beveiligde methoden en attributen gebruiken. privé -> alleen voor de klasse zelf bedoeld privé = geen enkel ander object dan het object zelf de methode kan aanroepen privé-methoden niet beveiligd maken omdat een subklasse er misschien ooit toegang toe zal willen hebben.
Mechanisme van overerving: Vervangen Gebruik beveiligd alleen voor die methoden waarvan u weet dat een subklasse deze wil gebruiken; anders privé of publiek strenge werkwijze => daardoor later misschien naar code teruggaan om toegangsniveau van een methode te wijzigen. Deze werkwijze leidt echter tot een strakker ontwerp dan een werkwijze die alles voor een subklasse openstelt.
Mechanisme van overerving: Vervangen = geen slechte werkwijze (terugkeren en bijwerken van toegangsniveau) maar overervingshiërarchieën mogen nooit per ongeluk optreden: hiërarchieën moeten zich op een natuurlijke manier ontwikkelen tijdens programmatie bijwerken = normaal want echte objectgeoriënteerde programmeren is een stapsgewijs proces vuistregel = alles privé te maken => soms geen voordeel => afhankelijk van wat u aan het programmeren bent: bijvoorbeeld bibliotheken met algemene klassen om te verkopen-> standaard beveiligd.
Mechanisme van overerving: Vervangen => afhankelijk van wat u aan het programmeren bent: bijvoorbeeld bibliotheken met algemene klassen om te verkopen-> standaard beveiligd klanten kunnen overerving gebruiken voor het uitbreiden van klassen. ook subklassen ontwerpen met overerving in gedachten overervingsprotocol = abstracte structuur alleen zichtbaar via beveiligde elementen van klasse
Mechanisme van overerving: Vervangen gevolg: alleen beveiligde en publieke methoden en attributen zijn het belangrijkst voor de overerving kan zich recursief gedragen: de vervangen methode in de child kan de versie van de methode uit de parent aanroepen =>mogelijkheid om versie van de superklasse te gebruiken terwijl nieuw gedrag in subklasse wordt gedefinieerd -> gebruik van sleutelwoord super
Mechanisme van overerving: Nieuwe methoden en attributen komen enkel in child, voor niet in parent => nieuwe functionaliteit toevoegen aan interface van child Recursieve methoden en attributen wordt gedefinieerd in parent of andere voorouder maar niet in child bericht wordt door de hiërarchie omhoog doorgegeven tot definitie van de methode wordt gevonden -> zie vroeger
Soorten overerving: 3 manieren waarop overerving kan gebruikt worden: 1. voor het hergebruiken van implementatie; 2. voor het verschil; 3. voor typevervanging. Wees gewaarschuwd, sommige soorten hergebruik zijn meer wenselijk dan andere! Laten we elk van deze gebruiksdoelen eens in detail bekijken.
Soorten overerving: Overerving voor implementatie door overerving komt code uit parent automatisch beschikbaar voor child => niet nodig code te kopiëren en plakken men zit echter vast aan de overgeërfde implementatie -> eventueel ook negatieve effecten => vervangen van de beveiligde methoden kan de invloed op een slechte of ontoepasselijke implementatie verminderen
Soorten overerving: Overerving voor implementatie = zwakste vorm van overerving => child krijgt automatisch het type van de parent het op de juiste manier erven van het type moet altijd voorrang hebben tijdens het ontwerpen van klassenhiërarchieën we beschouwen een eenvoudige definitie van overerving: bij overerving wordt zowel de implementatie als de interface overgeërfd -> in sommige objectgeoriënteerde talen gaat dit apart
Soorten overerving: Overerving voor het verschil programmeren op het verschil -> enkel code wordt toegevoegd die de nieuwe klasse van de geërfde klasse laat verschillen gezien in voorbeeld met TwoDimensionalPoint en ThreeDimensionalPoint ; ook in Employee. ThreeDimensionalPoint verschilt van parent door -> Z-coördinaat wordt toegevoegd -> 2 nieuwe ondersteunende methoden voor de Z- coördinaat voor het instellen en ophalen van deze eigenschap -> herdefinitie van de methode toString( )
Soorten overerving: Overerving voor het verschil daardoor stapsgewijs programmeren mogelijk kleinere, beter beheersbare code => eenvoudiger ontwerpen => minder fouten twee manieren van programmeren op verschil via overerving: -> door nieuwe gedragingen en eigenschappen toe te voegen ->door oude gedragingen en eigenschappen te herdefiniëren.
Soorten overerving: Overerving voor het verschil beide gevallen staan bekend als specialisatie specialisatie = het proces van een child-klasse die zichzelf definieert in termen van hoe deze van zijn parent verschilt door -> nieuwe methoden en eigenschappen toe te voegen -> herdefinitie bestaande methoden -> verwijderen methoden is onmogelijk
een klasse kan niet selectief erven Soorten overerving: Overerving voor het verschil een klasse kan niet selectief erven Specialisatie beperkt wat er wel en wat niet een driedimensionale punt kan zijn: een Three-DimensionalPoint kan altijd een TwoDimensional-Point zijn, maar een TwoDimensionalPoint kan nooit een ThreeDimensionalPoint zijn ThreeDimensionalPoint = specialisatie van TwoDimensionalPoint TwoDimensionalPoint = generalisatie van ThreeDimensionalPoint.
Soorten overerving: Overerving voor het verschil verschil tussen generalisatie en specialisatie: bij specialiseren -> omlaag door hiërarchie bewegen bij generaliseren -> omhoog door hiërarchie bewegen
Soorten overerving: Overerving voor het verschil specialiseren = niet beperken van de functionaliteit = beperken van het aantal typecategorieën men kan ingewikkelde ingewikkelde structuren van klassenhiërarchieën maken -> streven naar ondiepe hiërarchieën -> diepgaande hiërarchieën zijn moeilijker te onderhouden
Soorten overerving: Overerving voor het verschil voorouder = klasse die in de klassenhiërarchie vóór de parent van een gegeven child komt bijv. : Format een voorouder van DecimalFormat. afstammeling = klasse die na een andere gegeven klasse in de klassenhiërarchie voorkomt bijv.: DecimalFormat is afstammeling van Format. hoofdklasse (of root ) = de bovenste klasse in de overervingshiërarchie. bijv.: OneDimensionalPoint = een hoofdklasse
Soorten overerving: Overerving voor het verschil eindklasse (of leaf-klasse) = een klasse zonder children bijv.: DecimalFormat
Soorten overerving: Overerving voor het verschil
Soorten overerving: Overerving voor het verschil opmerking 1: -> afstammelingen weerspiegelen de in hun voorouders gemaakte wijzigingen bijv.: er is een fout in TwoDimensionalPoint => alle klassen van ThreeDimensionalPoint tot aan FourDimensionalPoint zullen van de wijziging profiteren als u de fout in TwoDimensionalPoint verhelpt verhelpen van fouten of efficiënter maken van implementatie => alle afgestamde klassen in de hiërarchie hebben daar voordeel van
Soorten overerving: Overerving voor het verschil opmerking 2: -> tot dusver alleen enkelvoudige overerving -> meervoudige overerving = een enkel object kan rechtstreeks van meer dan één andere klasse erven Meervoudige overerving = controversieel aspect van het objectgeoriënteerd programmeren: * sommigen beweren dat meervoudige overerving software alleen maar moeilijker te begrijpen, te ontwerpen en te onderhouden maakt * Anderen zweren erbij en beweren dat een taal zonder meervoudige overerving niet compleet is
Soorten overerving: Overerving voor het verschil opmerking 2: -> meervoudige overerving kan nuttig zijn, mits er voorzichtig en op de juiste manier gebruik van wordt gemaakt -> meervoudige overerving introduceert een aantal problemen. Een volledige bespreking van de voor- en nadelen van meervoudige overerving valt echter buiten het bestek van deze cursus.
Soorten overerving: Overerving voor typevervanging daardoor kunnen voor vervanging geschikte relaties gemaakt worden voorbeeld van een voor vervanging geschikte relatie: klasse Line
Soorten overerving: Overerving voor typevervanging
Soorten overerving: Overerving voor typevervanging
Soorten overerving: Overerving voor typevervanging
Soorten overerving: Overerving voor typevervanging
Soorten overerving: Overerving voor typevervanging
Soorten overerving: Overerving voor typevervanging Een voor vervanging geschikte relatie betekent dat elk willekeurig object dat van een TwoDimensionalPoint erft aan de constructor van Line kan doorgegeven worden. Reden: -> de “is een”-relatie een child “is een” parent ThreeDimensionalPoint 'is' een TwoDimensionalPoint.
Soorten overerving: Overerving voor typevervanging
Soorten overerving: Overerving voor typevervanging aan de constructor van de lijn wordt zowel een TwoDimensionalPoint als een ThreeDimensional- Point doorgegeven resultaat van deze main Midpoint : (14.0 , 14.0) Distance : 5.656854249492381
Soorten overerving: Overerving voor typevervanging Een voor vervanging geschikte relatie = geschiktheid voor inpluggen (pluggability) -> elk bericht dat naar parent kan gestuurd worden kan ook naar een child ervan gestuurd worden => child kan behandeld worden als zou deze uitwisselbaar zijn met zijn parent => daarom nooit gedragingen verwijderen tijdens het maken van een child
Soorten overerving: Overerving voor typevervanging Geschiktheid voor inpluggen ->nieuwe subtypen maken: als programma weet hoe het een voorouder moet gebruiken, dan weet het ook hoe het de nieuwe objecten moet gebruiken => programma hoeft het precieze type van het object niet te kennen => programma kan object gebruiken, zolang dat object maar een voor vervanging geschikte relatie tot het verwachte type heeft.
Soorten overerving: Overerving voor typevervanging Een voor vervanging geschikte relatie : enkel in de zin van de specialisaties => neerwaarts en niet opwaarts -> methode met als argument een bepaald type van een object => geen parent ipv het object; wel alle afstammelingen zijn geschikt.
Soorten overerving: Overerving voor typevervanging -> bijvoorbeeld de constructor van Line: + Line(cp1: TwoDimensionalPoint, cp2 : TwoDimensionalPoint) * TwoDimensionalPoint of een willekeurige afstammeling van TwoDimensionalPoint kan aan constructor worden doorgeven. * maar geen OneDimensionalPoint staat hoger in de klassenhiërarchie dan TwoDimensionalPoint
Soorten overerving: Overerving voor typevervanging Een subtype = een type dat een ander type uitbreidt via overerving. Geschiktheid voor inpluggen vergroot de kans op hergebruik: -> vb: u schrijft een array van TwoDimensionalPoint's => array ook voor elke willekeurige afstammeling van TwoDimensionalPoint ! Geschiktheid voor inpluggen is belangrijk, want hiermee kunt u algemene code schrijven. U kunt gewoon objecten schrijven die met objecten van het type TwoDimensionalpoint kunnen omgaan, in plaats van een aantal meervoudige ALS-structuren (case in Java) of ALS-DAN-controles te moeten schrijven om te zien wat voor soort objecten er momenteel in het programma worden gebruikt.
Soorten overerving: Overerving voor typevervanging Geschiktheid voor inpluggen: belangrijk -> objecten schrijven die met objecten van het type TwoDimensionalpoint kunnen omgaan ipv meervoudige ALS-structuren (case in Java) of ALS-DAN-controles
Tips voor effectieve overerving: Op een juiste manier gebruik maken van overerving gebruik altijd de “is een” -regel overerving voor hergebruik van interfaces en voor het definiëren van voor vervanging geschikte relaties voor het uitbreiden van een implementatie -> alleen als voldaan is aan de “is-een”-test hergebruik van implementatie -> in eenvoudige gevallen: samenstelling gebruiken alleen overerving als de 'is een'-test op de resulterende hiërarchie kan toegepast worden
Tips voor effectieve overerving: Goede overervingshiërarchieën ofwel hiërarchieën ontdekken terwijl u werkt -> herschrijf dan uw code ofwel hiërarchieën weloverwogen ontwerpen => te volgen ontwerpprincipes vuistregel: klassenhiërarchieën -> relatief ondiep verplaats algemene dingen naar abstracte basisklassen
Tips voor effectieve overerving: Abstracte basisklassen: ->mogelijk methodes zonder implementatie te specificeren ->geen instanties mogelijk wegens eventueel ontbreken van implementatie ->abstractiemechanisme dwingt ervende klasse implementatie te leveren -> = nuttig voor geplande overerving: helpen ontwikkelaar zien wat moet geïmplementeerd worden
Tips voor effectieve overerving: Abstracte basisklassen: ->als taal geen abstractiemechanisme levert lege methoden maken en documenteren dat die methoden in hun geheel door subklassen moeten worden geïmplementeerd. gemeenschappelijke code in klassen => geen zin gemeenschappelijke code verwijderen en naar één enkele parent-klasse verplaatsen niet te ver omhoog in hiërarchie, alleen naar het eerstvolgende niveau boven het beschouwde niveau
Tips voor effectieve overerving: refactoring : -> moeilijk op voorhand hiërarchieën in hun geheel te plannen -> gemeenschappelijkheden zullen pas opvallen als dezelfde code een paar maal is geschreven herschrijven van klassen als gemeenschappelijk- heid opmerkt wordt = refactoring
Tips voor effectieve overerving: Goede inkapseling van parent en child Gebruik goed gedefinieerde interfaces tussen de parent en de child, zoals tussen andere klassen Methoden die specifiek bedoeld zijn voor gebruik door subklassen, worden beveiligd (protected) gemaakt, zodat alleen de subklasse ze kan zien. Daardoor krijgen de subklassen wat meer controle, zonder deze controle voor elke klasse beschikbaar te maken.
Tips voor effectieve overerving: Goede inkapseling van parent en child interne implementatie van uw objecten mag niet voor subklassen open gesteld worden, anders wordt subklasse afhankelijk van die implementatie. Afsluitende tips voor effectieve overerving: vervanging = voornaamste doel van overerving -> als een object 'intuïtief' in een hiërarchie lijkt thuis te horen, daarom is dit niet noodzakelijk zo en daarom moet u het ook werkelijk niet doen.
Tips voor effectieve overerving: Afsluitende tips voor effectieve overerving: Programmeer op verschil om code hanteerbaar te houden. Geef altijd de voorkeur aan samenstelling boven overerving voor het hergebruiken van implementatie =>de betrokken klassen kunnen makkelijker gewijzigd worden.