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()