Arv och klassbibliotek
Johan L Larsson
Uppsala Universitet/KTH
November 1999
[email protected]
1
Klasser och objekt
Car
p a rtic ula rC a r
in sta n tia te s
[email protected]
2
Introduktion till arv
• Problem med modifikation av slutna
moduler: man vill helst undvika att göra
ändringar.
• Det är naturligt att organisera många
problem hierarkiskt, dvs i olika nivåer.
• Arv är “delta-change”, dvs ändring genom
att ange skillnad mellan klasser.
• En subklass ärver från en superklass och
får då allt som superklassen har, plus
mer!
[email protected]
Exempel
Vehicle
Land Vehicle
Car
Truck
3
Water Vehicle
Boat
Submarine
Air Vehicle
Airplane
Rocket
Antag att Vehicle har:
• Attributet fSpeed
• Metoden accelerate()
Då har alla klasserna åtminstone:
• Attributet fSpeed
• Samma metod accelerate()
… men dom kan ha mer!
[email protected]
4
Olika typer av arv
• Enkelt arv (“single inheritance”)
– Klasserna bildar hierarkier där varje klass har
endast en superklass.
• Multipelt arv (“multiple inheritance”)
– Klasserna bildar komplexa grafer där en klass
kan ärva från flera andra klasser.
– Problem med entydighet, komplicerade
beroenden, etc, gör att Java saknar detta.
• Interface-”arv” (som i Java)
– Ren subtypning (utan att kod ärvs)
[email protected]
5
Typer - 1
• En typ är en mängd värden och en mängd
operatoner på dessa värden.
• Typer låter oss på ett säkert sätt para ihop
operationer med de värden där de tillåts.
• Typfel kan antingen
hanteras av kompilatorn
O bject
eller under körningen.
• = statisk (“strong”) respektive
(“weak”) dynamisk typning
Message
Type error
(message not found)
[email protected]
6
Typer - 2
• Typer ger oss säkrare programmering.
• Statisk eller dynamisk typning? Industrin
föredrar traditionellt statisk typning som i
Ada, Pascal, C, C++, Java, Eiffel m fl:
+ Undviker körningsfel redan vid kompilering
+ Gör program mer tillförlitliga i potentiellt
farliga situationer, t ex i ett flygplan.
+ Snabbare körning (typkontroll är kostsamt)
− Mindre flexibel programvara (jfr Smalltalk)
[email protected]
7
Konstruktion av (sub-) typer
• Viktig egenskap hos OO PL: användare kan
bygga nya typer från gamla:
— Genom att “köpa”, dvs låta klasser innehålla
attribut av andra klasser precis som strukturer i
Pascal eller C (record/struct).
Genom arv. Nya subtyper V ehicle
ärver från gamla typer,
a
is eventuellt med tillägg
C ar
h a s-a
W heel
(attribut, metoder).
—
[email protected]
8
Dynamisk bindning
• Bidning: ett väre måste ges en typ innan
den kan användas.
• På samma sätt måste objekt ges en klass
innan de mottar meddelanden, etc.
• Dynamisk bindning = ett objekt ges en
klass vid tilldelning, ej vid deklaration.
[email protected]
9
”Principle of Substitutability”
• Instanser av en subklass innehåller alla
attribut som finns i en superklass.
• Instanser måste ”kopiera” superklassens alla
metoder.
• Därför kan en subklass härma en superklass så att vi kan behandla dem lika.
If we have two classes A and B, such that class B is a
subclass of A, it should be possible to substitute instances
of clss B for instances of class A in any situation with no
observable effect. [Budd96]
[email protected]
10
Specialisering med arv
• En subklass kan vara en specialisering av
superklasen. T ex är ”bil” en specialisering
av ”fordon”.
• ”Window” kan specialiseras till
”TextWindow”
• Raffinering (”refinement”) av
superklassen
• Subklassen blir också en subtyp
• Subklassen kan bytas ut mot superklassen
närsomhelst.
[email protected]
11
Arv som specifikation
• Arv kan garantera att
Mer
Mer
en subklass uppfyller
generell
specifik
en specifikation
(gränssnitt, etc).
• Till skillnad från
”specialisering” är
• Abstrakta klasser är
superklassen inte
klasser som inte kan
komplett utan måste
användas - dom
fyllas med funktion,
saknar metoder etc
helt eller devis.
[email protected]
12
Arv som konstruktion (är-del-av)
• En klass kan ärva från en eller flera
klassar för att få all sin funktionalitet.
• Man plockar då in beteende från klasser av
intresse och använder arv för detta.
• Bryter ofta ”principle of substitutability”:
subklasser blir inte alltid subtyper.
• Privat arv löser problem med arv för
konstruktion.
[email protected]
13
Arv som generalisering
• Motsats till specialisering
• Exempel: ColoredWindow ärver från
Window och blir ett mer generaliserat
fönster. Det har ju färg.
• Bygger ofta på tillägg av attribut, t ex färg i
exemplet.
• Bör undvikas: vänd istället typhierarkin.
[email protected]
14
Arv för utökning
• Utökning (”extension”) = expandera med
funktionalitet i superklassen som är helt
nytt (dvs ej raffinering av gamla funktioner)
• StringSet som kan tänkas ärva Set
tillhandahåller säkert flera helt nya
metoder. De är utökningar av superklassen.
• Finns någon fördel med att lämna gammal
funktionalitet ”som den är”?
[email protected]
15
Arv för kombination
• Man vill ibland kombinera funktionalitet
ifrån flera klasser i en enda klass
(multipelarv)
• Detta kan ge problem om funktionalitet
kolliderar (t ex metoder har samma namn)
[email protected]
16
Arv för begränsning
• Om en klass ärver från en superklass
med funktionalitet som ej behövs kan
subklassen begränsas
• Man kan då överskriva med tomma metoder
och eventuellt göra anrop till programfel.
• Bör aldrig användas! (Varför?)
[email protected]
17
En liten slutsats
• Arv kan användas på ett dåligt
sätt om vi inte tänker oss för!
[email protected]
18
Överskrivning
• Att ersätta en metod med en ny metod genom
arv kallas överskrivning
• En form av polymorfism som baserar sig på
dynamisk bindning
• Vi använder virtual (Java, C++, ObjectPascal) för att markera vilka metoder som inte
binds fast då vi deklarerar klasser
• Virtuella metoder binder meddelanden till
objekts klasser under körningen
[email protected]
19
Kovarians
• Överskrivning av en metod med en
parameter som blir en subklass, dvs
parametern ”följer efter arvsriktningen”.
se tD rive r (D rive r d );
Driver
Vehicle
Mer
generell
TruckDriver
Car
Truck
se tD rive r (T ru ckD rive r d );
• Kan ge problem med typkompatibilitet
(Truck.setDriver accepterar bara en delmängd av typerna)
[email protected]
20
Kontravarians
• Överskrivning av en metod där parametern
blir en superklass.
setDriver (TruckDriver d);
Driver
Vehicle
Mer
specifik
TruckDriver
Car
Truck
setDriver (Truck d);
[email protected]
21
Avancerad överskrivning
• Vi kan ställa krav på överskrivningen:
– En viss grupp av metoder måste överskrivas
samtidigt.
– Inramade metoder: tillåt överskrivning med
”before method” eller ”after method” (CLOS)
– Kovarians och kontravarians.
[email protected]
22
Multipelarv - 1
• ”Diamond-inheritance”
+ om man tänker sig arv som
kombination av beteenden
i superklasser blir
multipelarv en naturlig
utökning. Ex. C++ och
Eiffel.
+ Modellering blir nu
annorlunda - vad som var
svart med enkelt arv blir nu
lätt.
N um ber
M agnitude
Integer
[email protected]
23
Multipelarv - 2
– Tvetydighet med namn:
• Byt namn utan att ändra metod
• Definiera om metoder/attribut
– Delad superklass:
• Dubbla kopior av attribut etc.
• Virtuella superklasser som i C++
[email protected]
S tream
In S tream
O u tStre am
In O u tStre am
24
Några fler problem med arv
• Hur kan vi ändra klasser mitt i hierarkier, eller
är vi begränsade till utkanten av
hierarkierna?
• Är det lätt att läsa långa klasshierarkier där ett
visst objekts beteende kan härstamma från
väldigt många klasser? Jojo-effekt.
• Kan vi använda en enskild klass i ett annat
program eller måste vi flytta över en hel
hierarki av klasser?
• Blir verkligen subklass en subtyp? Kontrakt?
[email protected]
25
Alternativ till arv
• Enkelt arv (”single inheritance”)
– Komplettera med nästade klasser
– Komplettera med interface-arv
• Multipelt arv
– Ger stora möjligheter men ibland problem
• Inget (implementations-) arv:
– Subtyper (interface-arv)
– Has-a relationer (klasser som attribut)
[email protected]
26
Klassbibliotek och hierarkier
• Språk såsom Java och Eiffel tillhandahåller
ett stort antal klasser i ett så kallat
klassbibliotek.
• Klassbibliotek baserar sig på arvsrelationer.
• Klassbibliotek ska inte ändras. Vi använder
arv för att överskriva beteende.
• Klassbibliotek kan använda abstrakta
klasser som vi måste utöka beroende på vad
vi exakt vill använda biblioteket till.
[email protected]
27
Klassbiblioteket i Eiffel
Array
Array
Fixed
Fixed
List
List
List
List
Node
Node
List
List
Linked
Linked
List
List
Bi-List
Bi-List
Node
Node
Bi-Linked
Bi-Linked
List
List
Tree
Tree
Node
Node
[email protected]
28
Att lära av andra = ”reuse”
• Återanvändning!
• Genom att använda arv kan vi använda
klassbibliotek utan att göra modifikationer.
• Vi slipper förstå detaljer om klasser och
behöver bara första ett fåtal metoder även
om en mängd metoder används.
[email protected]
29
Att bygga klassbibliotek
• Det är svårt att bygga bra klassbibliotek!
– Klasserna måste vara tillräckligt öppna för
utökningar och anpassningar
– Klasserna får inte vara inlåsta i arv (vi behöver
has-a relationer för att det ska bli flexibelt)
– Vi måste välja rätt arvsrelationer: t ex vad ska
egentligen finnas i den gemensamma superklassen
för alla datastrukturer
– Vi måste förstå hur biblioteket ska användas!
[email protected]
30
Ett steg mot frameworks
• Objekt-orienteade frameworks är ett
aktuellt forskningsområde!
• Ett framework är ett bibliotek av klasser
som vävts mycket tätt samman. De bildar en
”abstrakt applikation” med extremt mycket
gömda detaljer.
• Frameworks är svåra att konstruera, svåra
att lära sig - men mycket kraftfulla för
återanvändning!
[email protected]
31
Nästade klasser
• Klasser kan vara medlemmar i andra klasser i t ex
Java och C++:
class EnclosingClass {
...
class NestedClass {
…
}
}
• Nästade klasser löser problem med encapsulation: den
nästade klassen kommer åt den yttre klassens medlemmar,
även de som är deklarerade som private.
• Underlättar organisering av kod.
[email protected]
32
Motivation till nästade klasser
// An example with cluttered but valid code
public class MyClass implements MouseListener {
...
someObject.addMouseListener(this);
...
/* Empty method definitions. */
public void mousePressed(MouseEvent e) { }
public void mouseReleased(MouseEvent e) { }
public void mouseEntered(MouseEvent e) { }
public void mouseExited(MouseEvent e) { }
public void mouseClicked(MouseEvent e) { }
}
• Svårt att utöka och mycket kod att skriva.
[email protected]
33
Nästade klasser i Java
• Vi vill ha en inre klass MouseAdapter som vi
senare kan utöka.
// An example of using an inner class
public class MyClass extends Applet {
...
someObject.addMouseListener(new MyAdapter());
...
class MyAdapter extends MouseAdapter {
public void mouseClicked(MouseEvent e) { … }
}
}
• Annat exempel: iterator som inre klass till
datastrukturer
[email protected]
34
Polymorfism: en överblick
P olym orphism
A d hoc
C oercion
U niversal
O verloading
Inclusion
S ub-type
Code
P aram etric
O b je cts
[email protected]
35
Några uppgifter
• Uppgift 1: ge konkreta exempel på när det är
bättre med arv än komposition (has-a)!
• Uppgift 2: ge konkreta exempel på när det är
bättre med komposition (has-a) än arv!
• Uppgift 3: ge motivering till varför en subklass
inte behöver vara en subtyp (med exempel)!
[email protected]
36