H9: Klasse Ontwerp Richtlijnen Specificaties Multiple inheritence SchetsPlus... doe ik het goed ? 2 Hoe maak ik goede klassen ? We gaan kijken naar: algemene ontwerp-richtlijnen software metric Complement: style-gidsen, tips, best practices, kookboeken, enz. Sun’s Java Coding Style Guide Ambler, the Elements of Java Style 3 Richtlijnen Structuur is belangrijk, wat je niet wil: complex fouten, onderhoudkost star Optimaliseer : encapsulatie inheritence cohesie koppeling 4 Cohesie Een “module” is cohesief als het een set van sterk aan elkaar gerelateerd “functionaliteiten” aanbiedt. Persoon getGewicht() getLengte() Persoon getGewicht() getLengte() getVrienden() Persoon getGewicht() getLengte() addInAdresBoek(aboek) 5 Koppeling Er is een koppeling tussen modules A en B als een van de andere afhankelijk is. Voorbeelden: datakoppeling f() { ... u.g(x) ... } globale-var koppeling • m1,m2 via een static attribuut •C1, C2 via een package-private attribuut 6 Koppeling Pathologisch Drank - suiker : int - water : int + zoet() : int + roer() in C++ heb je ook friends. 7 Mixer if (drank.zoet() > 10) { drank.water++ ; drank.roer() ; } Koppeling In OO ook door: Associatie / navigatie Verse objecten in methode Via inheritence Vaak spanningveld tussen koppeling en de andere aspecten: maak A subklasse van B koppeling verhoogt cohesie met delegatie koppeling 8 Inheritence koppeling Persoon getKinderen() Klant Drank mixMetMelk() // concreet abstract mix() Thee Koffie 9 Demeter Principe Ian Holland, 1987 “Zorg dat objecten alleen met vrienden praten.” Vliegtuig bagageGewicht() : int vervoert * Persoon naam heeft * Tas gewicht Delegeer: Vliegtuig vervoert * bagageGewicht() : int Persoon naam tassenGewicht() heeft ten koste van de cohesie van Persoon. 10 * Tas gewicht Connascance Page-Jones, 1992. Letterlijk: tegelijkertijd geboren. Page-Jones: Klassen C en D zijn connascent als het mogelijk is om C aan te passen die een aanpassing van D dwingt. koppeling! 11 Uit project management perspectief Software metriek complexiteit indicatoren Om strategisch te beslissen dat bepaalde delen van de software een risico factor zijn, en dat reorganisatie nodig is. Voor programmeurs ook nuttig als richtlijnen. 12 Voorbeeld Metriek : + Uit te rekenen (en goedkoop) tools! - abstract Zoals, #regels Nog meer? Traditionele metrieken Halstead McCabe Oviedo OO metriek “de” metriek bestaat niet ze zijn allemaal indicatoren. 13 Halstead Complexiteit: moeite om code te lezen E = D*V x = x + x ; x++ x = x + y ; z++ (alleen ter info) 14 McCabe Het aantal lineair onafhankelijke paden in je programma. int P() { if (...) return 100 else return 0 } 0 1 2 Control Flow Graph (CFG) 15 McCabe 0 4 1 3 2 5 16 Oviedo Splits programma in sequentiële blokken interacties tussen elementen in een blok voegen niets aan complexiteit. afhankelijkheden tussen blokken wel. Voorbeeld: P(int x, int y) { if (y>0) x = 0 ; else x=1; (y>0) ; x = 0 (~y>0) ; x = 1 DF = 2 DF = 2 return x } 17 return x DF(P) = 6 DF = 2 OO metriek Voor OO willen we ook indicatie hebben over structurele complexiteit van je klassen. Chen & Lu, 1993: Encapsulatie Koppeling Inheritence Cohesie 18 Encapsulatie (P) Idee: Methode met minder argumenten is abstracter Simpel vs complex parameters, bijvoorbeeld: Type Complexiteit waarde boolean, int 0 double 2 object 6-9 P = som van de complexiteit van de argumenten (van pub. methodes) in C. 19 Koppeling (Cp) Idee C gebruikt D 1x koppeling C wordt door D gebruikt 1x koppeling wederzijde koppeling lastig telt als extra Cp = som van boven. 20 Cohesie Co Hoe weten we welke methode bij elkaar horen?? Idee: methodes met dezelfde type signatuur horen vaak bij elkaar. Chen en Lu ook “sub” signatuur. m1(int,Vervoer) ~ m2(Vervoer,int,Persoon) Vervoer + versnel(real) + addPassagier(Persoon) + swapPassagiers(Persoon, Persoon) + addPassagiers (Collection<Persoon>) 21 Co = G / N = 0.75 Inheritence H Inheritence is goed (code hergebruik), maar je code wordt ook minder expliciet in zekere zin ook fout gevoelig. H is een meting van inheritence complexiteit. Som van: # methodes inheritence afstand # direct superklassen. # subklassen 22 JHawk 23 Klasse specificatie Minderjarig bonus Klant Naam Leeftijd Videotheek leent * DVD titel leeftijdgrens < is verantwoordelijk voor Meer kun je niet met klasse diagram uitdrukken... Een klant is een mj als zijn/haar leeftijd ≤ 18 De klant die voor een mj verantwoordelijk is, is zelf geen mj. 24 . Klasse invariant Een klasse invariant van C is een constraint op de attributen van de objecten van C. Anders zit een object in een verkeerde/onveilig toestand. Minderjarig Bob 10 Minderjarig Octo 30 De leeftijd van een mj is 18. Het gaat over de stabiele toestand van een object 25 (dus niet als een operatie nog bezig is met het object). Klasse invariant Maar indirect gaat het eigenlijk ook over associaties... DVD Sneeuwwit 0+ Minderjarig Bob 10 DVD Star Trek 10+ DVD Kill Bill 12+ Hoe belangrijk? erg belangrijk. 26 Hoe druk je dat uit? Met “predicaten” zoals in Logica : (forall x : Minderjarig x.leeftijd 18) “Object Constraint Language” (OCL) onderdeel van UML. context x : Minderjarig inv: x.leeftijd 18 27 Navigatie in OCL Klant Naam Leeftijd leent > lener * dvds DVD titel leeftijdgrens context dvd : DVD inv: dvd.lener.leeftijd dvd.leeftijdgrens context x : Klant inv: x.leeftijd x.dvds.leeftijdgrens levert een verzameling terug! 28 Collecties in OCL Zoals Set en Sequence Operatoren zoals: size(), sum(), isEmpty() includes(x) forAll(...), exists(...), select(…) Eigenaardig syntax: u isEmpty() u includes(x) 29 Voorbeeld Klant Naam Leeftijd leent lener * dvds DVD titel leeftijdgrens context x : Klant inv: x.leeftijd x.dvds.leeftijdgrens inv: x.dvds forall (dvd | x.leeftijd dvd.leeftijdgrens) 30 Filter select operatie Klant Naam Leeftijd leent lener * dvds DVD titel premium : boolean Je mag slechts één permium DVD lenen. context x : Klant inv: x.dvds select (dvd | dvd.premium = true) size() ≤ 1 31 Methode specificeren Klant inschrijf() leen(d) getLeenLimiet() Pre/Post spec, pseudo code, geen OCL. declaratief ! Inschrijf() dient de collectie van gelende dvds op leeg te zetten forall x : Klant { true } x.inschrijf() { x.dvds = } context x : Klant:: inschrijf() pre: true post: x.dvdsisEmpty() 32 Mix in specificatie met “query” Klant inschrijf() leen(d) getLeenLimiet() Je mag niet meer dan je limiet lenen. markeer als ‘isQuery’, maar alleen als side-effect vrij! context x : Klant:: leen(d) pre: x.dvds size() + 1 ≤ x.getLeenLimiet() post: x.dvds = x.dvds@pre insert(d) 33 Multiple inheritence Product ID Naam Prijs GroteBestelling koopGroot(n) ImportGoed invoerBelasting() Koffie Krachtig ! Je kunt makkelijk verschillende features erven. Talen met MI C++. Eiffel, Python Om uit te kijken diamantprobleem 34 Als je toch in Java wil implementeren … Feature clash… Werknemer werk() Welke werk() wordt bedoelt in Muzikant ? (soms de ene soms de andere) Artiest werk() Muzikant Geen echte issue. Eiffel renaming class Muzikant inherit Werknemer rename werk as werk1 Artiest rename werk as werk2 feature … end 35 Diamantprobleem Persoon Naam Leeftijd Werknemer Artiest Muzikant Een muzikant kan in principe 2x namen en leeftijden erven. leefdtijd onzinnig merge tot 1x naam misschien wil je een echte naam en een artiestnaam. Ambigu compiler kan deze niet zelf beslissen… 36 Inheritence vs associatie W Artiest Naam Werk() Artiest Naam Werk() Muzikant Muzikant Feature erven Is M ook een A ? 37 MI Ja Delegatie Ja Ja Nee Simuleren met assoc + interface Artiest Naam Werk() W Gedoe .. Als MI essentieel in je ontwerp is, implementeer ook in een MI taal. Muzikant Anders haal MI uit je ontwerp. W Artiest (Interface) Werk() Muzikant 38 Andere overweging: persistence. Artiest Naam Werk()