Title: Konzepte der objektorientierten Programmierung
1Konzepte der objektorientierten Programmierung
2Simulation von Ampelsystemen
"Objektorientierung ist die derzeitige Antwort
auf die gestiegene Komplexität der
Softwareentwicklung." Oestereich
Objektorientierte Software-Entwicklung
Wir betrachten hier Ampelanlagen zur Regelung des
Verkehrs an einer Straßenkreuzung - so, wie man
es überall in der Welt vorfindet. Ziel ist es
hier, solche Ampelanlagen mit Hilfe von
Softwarebausteinen zu simulieren. Dabei sollen
die Grundideen und Fachkonzepte der
objektorientierten Programmierung Schritt für
Schritt entwickelt werden.
3Teil 1
Objekte und Klassen
4Verwaltung von Ampeln
Wir betrachten hier eine Straßenkreuzung mit 4
Ampeln. Ziel ist es hier, dieses recht einfache
Ampelsysteme mit Hilfe von Softwarebausteinen zu
simulieren. Bevor der Computer die Simulation
übernehmen soll, spielen wir erst einmal die
anstehende Datenverwaltung und Datenverarbeitung
mit Personen durch.
5Rollenspiel zur Simulation
Die Datenverwaltung und Datenverarbeitung wird
für jede der 4 Ampeln von einer Person
übernommen. Diese Person erhält hierzu einen
Zettel, auf dem sie sich bestimmte Daten notieren
kann. Zusätzlich gibt es eine Person, die die
Arbeit der Ampeln jeweils aktiviert. Diese Person
kann den "Ampel-Personen" spezielle, vorab
festgelegte Anweisungen erteilen.
An ampel1
ampel1
6Rollenspiel zur Simulation
Aufgabe Damit das Rollenspiel funktioniert,
müssen zuerst Vereinbarungen getroffen werden.
Überlegt euch vorab, was die "Ampel-Personen" auf
dem Zettel notieren sollen und welche Anweisungen
die "Ampel-Personen" ausführen können soll.
Beachtet, dass die Ampeln zu Beginn in einen
Ausgangszustand gebracht werden müssen.
An ampel1
ampel1
7Ein Ampel-Objekt zur Simulation
Welche Daten muss ein Software-Objekt zur
Simulation einer Ampel verwalten? Ein solches
Software-Objekt Ampel muss sich in irgendeiner
Form "merken", in welcher Phase sich die
simulierte Ampel aktuell befindet. Hierzu kann
dieses Software-Objekt z.B. die Zustände der drei
Lampen registrieren. Welche Operationen muss ein
Software-Objekt zur Simulation einer Ampel
ausführen können? Zum einen muss es eine
Operation geben, mit der man ein Software-Objekt
in einen Ampel-Anfangszustand versetzen kann. Zum
anderen muss es eine Operation geben, mit der man
das Weiterschalten der Ampel bewirken kann.
8Ein Bauplan für Ampel-Objekte
class Ampel(object) def __init__(self)
self.lampeRot False self.lampeGelb
False self.lampeGruen False def
setLampen(self, startwertLampeRot,
startwertLampeGelb, startwertLampeGruen)
self.lampeRot startwertLampeRot
self.lampeGelb startwertLampeGelb
self.lampeGruen startwertLampeGruen def
schalten(self) if (self.lampeRot,
self.lampeGelb, self.lampeGruen) (True, False,
False) self.lampeGelb True
elif ...
Aufgabe Ergänze zunächst die Klassendeklaration
und speichere sie in einer Datei mit geeignetem
Namen (z. B. ampel.py) ab.
9Ampel-Objekt in Aktion
gtgtgt gtgtgt ampel1 Ampel() gtgtgt ampel1.lampeRot Fals
e gtgtgt ampel1.lampeGelb False gtgtgt
ampel1.lampeGruen False gtgtgt ampel1.setLampen(True,
False, False) gtgtgt ampel1.lampeRot True gtgtgt
ampel1.lampeGelb False gtgtgt ampel1.lampeGruen False
gtgtgt ampel1.schalten() gtgtgt ampel1.lampeRot True gtgt
gt ampel1.lampeGelb True gtgtgt ampel1.lampeGruen Fals
e
Aufgabe (a) Welche Anweisung erzeugt hier wohl
das Objekt ampel1? Wie inspiziert man den Zustand
der verwalteten Lampen? Wie aktiviert man das
Objekt, so dass es eine der in der Klasse
festgelegten Operationen ausführt? (b) Führe den
gezeigten Python-Dialog aus und setze ihn so
fort, dass ein gesamter Ampelzyklus durchlaufen
wird. Kontrolliere so, ob die vervollständigte
Klassendeklaration eine Ampel korrekt beschreibt.
10Ampel-Objekt in Aktion
class Ampel(object) def __init__(self)
... def setLampen(self,
startwertLampeRot, startwertLampeGelb,
startwertLampeGruen) ... def
schalten(self) ... Test ampel1
Ampel() ampel1.setLampen(True, False,
False) print(ampel1.lampeRot, ampel1.lampeGelb,
ampel1.lampeGruen)
Aufgabe Das Verhalten von Objekten kann man auch
mit einem Programm testen. (a) Führe das
gezeigte Testprogramm aus und erkläre sein
Verhalten. (b) Das zu simulierende Ampelsystem
(siehe oben) besteht aus 4 Ampeln. Ändere das
Testprogramm so ab, dass ein kompletter
Ampelzyklus des Ampelsystems simuliert wird.
11Objekte unserer Welt
Objekte unserer Welt sind fassbare Gegenstände
(wie z.B. Schuhe oder Kartenstapel) oder auch
Konstrukte unseres Denkens und Handelns (wie z.B.
Schuhladen oder Kartenspiel). Betrachten wir das
Beispiel "Schuhe". Wenn man seine neuen Schuhe
charakterisieren will, dann fallen einem sofort
eine Reihe von Eigenschaften ein Modell
Sneaker Größe 40 Farbe rot Verschluss
Schnürsenkel usw.. Schuhe haben nicht nur
bestimmte Eigenschaften, mit ihnen kann man auch
bestimmte "Operationen" ausführen Zum An- und
Ausziehen kann man sie in einem gewissen Sinn
öffnen und schließen (z.B. durch Öffnen und
Binden der Schnürsenkeln). Vielleicht kann man
sie auch benutzen, um einen Nagel einzuschlagen,
obwohl sie dafür eigentlich nicht gedacht
sind. Objekte prägen sehr stark unser Denken.
Wir können die Welt, in der wir leben, mit Hilfe
von Objekten beschreiben, die Eigenschaften haben
und die mit bestimmten Operationen bearbeitet
werden können.
12Software-Objekte
Analog zu einer Ampel (als Objekt der realen
Welt) soll ein Software-Objekt ampel konzipiert
werden. Das folgende Ablaufprotokoll verdeutlicht
die Arbeitsweise eines Software-Objekts
ampel Das Software-Objekt verwaltet Daten zu
bestimmten Eigenschaften einer Ampel (im
vorliegenden Fall ist das die Eigenschaft, ob die
jeweilige Lampe an oder aus ist). Das
Software-Objekt ampel nutzt hierzu die Variablen
lampeRot, lampeGelb und lampeGruen. Das
Software-Objekt ampel stellt auch Operationen zur
Verarbeitung der verwalteten Daten bereit. Im
vorliegenden Fall gibt es z.B. Operation
weiterschalten", die durch die Prozedur
schalten() dargestellt wird. Das Software-Objekt
ist also eine Einheit, die Daten verwaltet und
Operationen zur Verarbeitung der verwalteten
Daten zur Verfügung stellt. Statt von
Software-Objekten wird im Folgenden kurz von
Objekten gesprochen.
13Fachkonzept - Objekt
Ein Objekt ist eine Einheit, die Daten mit Hilfe
von Attributen verwalten und Operationen zur
Verarbeitung der verwalteten Daten mit Hilfe von
Methoden ausführen kann. Attribute sind - an
Objekte gebundene - Variablen zur Verwaltung von
Daten. Diese entsprechen in der Regel den
Eigenschaften der betreffenden Objekte. Methoden
sind - an Objekte gebundene - Prozeduren oder
Funktionen zur Verarbeitung von Daten. Diese
Methoden werden ausgeführt, wenn das betreffende
Objekt Operationen ausführt. Ein Objekt befindet
sich stets in einem bestimmten Zustand. Der
aktuelle Objektzustand wird durch die aktuellen
Werte der Attribute festgelegt.
Objekt
Attribute - Attributwerte
Ausführung einer Methode
Objektdiagramm
14Fachkonzept - Objekt
Zugriff auf Attribute
Objekt
objekt.attribut
ampel1.lampeGruen
Aktivierung von Methoden
objekt.methode
Attribute - Attributwerte
ampel1.schalten()
Ausführung einer Methode
Objektdiagramm
15Klasse als Bauplan
Der Begriff "Klasse" wird hier im Sinne von
Klassifizieren benutzt. Du weißt sicher, was das
heißt Wenn man klassifiziert, dann versucht man,
Gemeinsamkeiten von Objekten herauszustellen. Schu
he Schuhe Schuhe Die Klasse "Schuh" beschreibt
Objekte, die man als Fußbekleidung nutzt - und
somit an- und ausziehen sowie tragen kann - und
die bestimmte Eigenschaften (wie Modell, Größe,
Farbe und Verschluss) aufweisen. Klassendiagramm
Wer Schuhe herstellen will, muss sich (mehr oder
weniger) an der Klassenbeschreibung für Schuhe
orientieren, damit das, was hergestellt wird,
auch wirklich Schuhe sind. Es macht sicher keinen
Sinn, sich an der Klassenbeschreibung für Hosen
oder Pullover zu orientieren. Eine
Klassenbeschreibung für Schuhe kann somit als
eine Art Bauplan für Schuhe aufgefasst werden.
16Fachkonzept - Klasse
Eine Klasse ist ein Bauplan für Objekte. Dieser
Bauplan legt genau fest, welche Attribute die zu
konstruierenden Objekte haben sollen und welche
Methoden sie ausführen können sollen.
Klassendiagramm
Ein Objekt (als Exemplar einer Klasse) ist eine
Einheit, die nach dem Bauplan der zugeordneten
Klasse erzeugt wurde. Ein Objekt verfügt somit
über die Attribute, die in der Klasse festgelegt
sind. Diesen Attributen können - im Unterschied
zur Klasse - Attributwerte zugewiesen werden. Ein
Objekt kann zudem sämtliche Methoden der Klasse
ausführen. Ausgenommen bleibt hier nur die
Methode, deren Name mit dem Klassennamen
übereinstimmt (s. u.). Objekte können mit Namen
versehen werden, über die sie dann gezielt
angesprochen werden können.
17Konstruktor / Destruktor
Zur Erzeugung von Objekten verfügt eine Klasse
über eine spezielle Methode, die sogenannte
Konstruktormethode. Zur Vernichtung von Objekten
verfügt eine Klasse über eine sogenannte
Destruktormethode.
Konstruktor
Ein Software-Objekt hat - wie Objekte der realen
Welt - eine bestimmte Lebensdauer. Es muss
erzeugt werden, bevor es in Aktion treten kann,
und kann auch wieder vernichtet werden. In einem
Klassendiagramm wird eine Konstruktormethode
dadurch gekennzeichnet, dass sie denselben Namen
wie die Klasse selbst trägt. Oft wird diese
spezielle Methode in Klassendiagrammen aber auch
weggelassen. Beachte, dass eine
Konstruktormethoden keine Methode ist, die ein
Objekt ausführen kann. Destruktormethoden werden
in der Regel in Klassendiagrammen weggelassen.
18Klassendeklaration in Python
Klassenname
Oberklasse
Schlüsselwort
Doppelpunkt
class Ampel(object) def __init__(self)
self.lampeRot False self.lampeGelb
False self.lampeGruen False def
setLampen(self, startwertLampeRot, )
self.lampeRot startwertLampeRot
self.lampeGelb startwertLampeGelb
self.lampeGruen startwertLampeGruen def
schalten(self)
Einrückung
Konstruktor
Attribute
Attribute
Methode
19Objekterzeugung in Python
gtgtgt ampel1 Ampel() gtgtgt ampel1 lt__main__.Ampel
object at 0x0136C4B0gt gtgtgt ampel1.__dict__ 'lampeR
ot' False, 'lampeGelb' False, 'lampeGruen'
False
Erzeugung eines Objekts
Inspektion eines Objekts
gtgtgt ampel1 Ampel() gtgtgt ampel1 lt__main__.Ampel
object at 0x0136C4B0gt gtgtgt del ampel1 gtgtgt
ampel1 Traceback (most recent call last) File
... ampel1 NameError name 'ampel1' is not
defined gtgtgt
Vernichtung eines Objekts
20Übungen
Aufgabe 1 Das Verhalten einer Ampel lässt sich
mit dem folgenden Zustandsdiagramm abstrahierend
beschreiben. Die Ampel befindet sich immer in
einem der Zustände "rot", "rotgelb", "gruen" oder
"gelb". Die Markierung am Zustand "rot" soll
bedeuten, dass sich eine Ampel zu Beginn in
diesem Zustand befindet. Mit der Aktion
"schalten" wird jeweils ein Zustandsübergang
ausgelöst.
class Ampel(object) def __init__(self)
self.zustand 'rot' def setZustand(self,
anfangszustand) self.zustand
anfangszustand def schalten(self)
if self.zustand 'rot'
self.zustand 'rotgelb' elif ...
def getLampen(self) if self.zustand
'rot' lampen (True, False, False)
elif ... return lampen
(a) Ergänze die Klassendeklaration. Teste sie
anschließend, indem du ein Objekt der Klasse
Ampel erzeugst und geeignet aktivierst. (b)
Zeichne ein Klassendiagramm, das zu dieser
Implementierung passt.
21Übungen
Aufgabe 1 (c) Wir ändern den Konstruktor der
Klasse Ampel wie folgt ab. Stelle Vermutungen
auf, was im folgenden Python-Dialog an Stelle der
Fragezeichen steht. Überprüfe deine Vermutung.
class Ampel(object) def __init__(self,
anfangszustand) self.zustand
anfangszustand def setZustand(self,
anfangszustand) self.zustand
anfangszustand def schalten(self)
if self.zustand 'rot'
self.zustand 'rotgelb' elif ...
def getLampen(self) if self.zustand
'rot' lampen (True, False, False)
elif ... return lampen
gtgtgt a1 Ampel('rot') gtgtgt a2 Ampel('gruen') gtgtgt
a1.getLampen() ? gtgtgt a2.getLampen() ? gtgtgt
a1.schalten() gtgtgt a2.schalten() gtgtgt
a1.setZustand('rot') gtgtgt a1.getLampen() ? gtgtgt
a2.getLampen() ?
22Übungen
Aufgabe 2 Entwickle und teste eine Klasse zur
Simulation von Fußgängerampeln.
Aufgabe 3 Viele Ampeln verhalten sich nachts
anders als tagsüber. Nachts wird nur die gelbe
Lampe aus- und eingeschaltet. Dieses Verhalten
soll mit einer weiterentwickelten Klasse Ampel
simuliert werden. Das Klassendiagramm ist wie
folgt gegeben (a) Mache dir zunächst klar, wie
die Tag-Nacht-Ampel funktionieren soll. (b)
Entwickle eine passende Implementierung und teste
sie.
23Teil 2
Modularisierung
24Das Bausteinprinzip
Modularisierung ist ein Prinzip, nach dem viele
Systeme entwickelt werden. Die Idee besteht
darin, das Gesamtsystem nach dem Baukastenprinzip
aus Einzelbausteinen (den sogenannten Modulen)
zusammenzusetzen.
"Unsere Partyzelte können in verschiedenen Größen
aufgebaut werden. Da die Partyzelte und Festzelte
aus Modulen bestehen, ist es sehr einfach, sie zu
erweitern. Die Abbildung zeigt ein mögliches
Kombinationsbeispiel der Module."
Ein Softwaremodul ist eine in sich abgeschlossene
Programmeinheit, die man vielfältig bei
Problemlösungen einsetzen kann. Grundidee der
objektorientierten Modularisierung ist es, solche
Softwaremodule als Klassen zu konzipieren.
Wir werden uns in diesem Abschnitt intensiver mit
Problemen auseinander setzen, die bei der
Verwendung von Klassen als Softwarebausteine
entstehen.
25Experimente mit Ampel-Objekten
def aktualisiereLampen(self) if
self.zustand 'rot' self.lampeRot
True self.lampeGelb False
self.lampeGruen False elif
self.zustand 'rotgelb'
self.lampeRot True self.lampeGelb
True self.lampeGruen False
elif self.zustand 'gruen'
self.lampeRot False self.lampeGelb
False self.lampeGruen True
elif self.zustand 'gelb'
self.lampeRot False self.lampeGelb
True self.lampeGruen False
class Ampel(object) def __init__(self)
self.zustand 'rot' self.lampeRot
None self.lampeGelb None
self.lampeGruen None self.aktualisiereLa
mpen() def getLampen(self) return
(self.lampeRot, self.lampeGelb,
self.lampeGruen) def schalten(self)
if self.zustand 'rot'
self.zustand 'rotgelb' elif
self.zustand 'rotgelb'
self.zustand 'gruen' elif self.zustand
'gruen' self.zustand 'gelb'
elif self.zustand 'gelb'
self.zustand 'rot' self.aktualisiereLamp
en()
Es gibt eine Vielzahl von Möglichkeiten, eine
Klasse Ampel zur Simulation einer solchen Ampel
zu konzipieren.
26Experimente mit Ampel-Objekten
gtgtgt a Ampel() gtgtgt a.zustand 'rot' gtgtgt
a.getLampen() (True, False, False) gtgtgt
a.schalten() gtgtgt a.zustand 'rotgelb' gtgtgt
a.getLampen() (True, True, False) gtgtgt a.zustand
'gruen' gtgtgt a.zustand 'gruen' gtgtgt
a.getLampen() (True, True, False)
class Ampel(object) def __init__(self)
self.zustand 'rot' self.lampeRot
None self.lampeGelb None
self.lampeGruen None self.aktualisiereLa
mpen() def getLampen(self) return
(self.lampeRot, self.lampeGelb,
self.lampeGruen) def schalten(self)
if self.zustand 'rot'
self.zustand 'rotgelb' elif
self.zustand 'rotgelb'
self.zustand 'gruen' elif self.zustand
'gruen' self.zustand 'gelb'
elif self.zustand 'gelb'
self.zustand 'rot' self.aktualisiereLamp
en()
Aufgabe Führe selbst diesen Dialog aus. Warum
ist dieser Dialog nicht im Sinne des
Ampelsystems? Woran liegt das?
27Experimente mit Ampel-Objekten
gtgtgt a Ampel() gtgtgt a.getZustand() 'rot' gtgtgt
a.getLampen() (True, False, False) gtgtgt
a.schalten() gtgtgt a.getZustand() 'rotgelb' gtgtgt
a.getLampen() (True, True, False) gtgtgt
a.setZustand('gruen') gtgtgt a.getZustand() 'gruen' gt
gtgt a.getLampen() (False, False, True)
class Ampel(object) def __init__(self)
self.zustand 'rot' self.lampeRot
None self.lampeGelb None
self.lampeGruen None self.aktualisiereLa
mpen() def setZustand(self, z)
self.zustand z self.aktualisiereLampen()
def getZustand(self) return
self.zustand def getLampen(self)
return (self.lampeRot, self.lampeGelb,
self.lampeGruen) def
schalten(self) if self.zustand
'rot' self.zustand 'rotgelb'
Kein Zugriff auf die Attribute
28Experimente mit Ampel-Objekten
gtgtgt a Ampel() gtgtgt a.getZustand() 'rot' gtgtgt
a.getLampen() (True, False, False) gtgtgt
a.schalten() gtgtgt a.getZustand() 'rotgelb' gtgtgt
a.getLampen() (True, True, False) gtgtgt
a.setZustand('gruen') gtgtgt a.getZustand() 'gruen' gt
gtgt a.getLampen() (False, False, True)
class Ampel(object) def __init__(self)
self.zustand 'rot' self.lampeRot
None self.lampeGelb None
self.lampeGruen None self.aktualisiereLa
mpen() def setZustand(self, z)
self.zustand z self.aktualisiereLampen()
def getZustand(self) return
self.zustand def getLampen(self)
return (self.lampeRot, self.lampeGelb,
self.lampeGruen) def
schalten(self) if self.zustand
'rot' self.zustand 'rotgelb'
Klasse mit Zugriffsmethoden
Aufgabe Warum ist es sinnvoll, dass Benutzer
einer Klasse nur die Methoden der Klasse
verwenden?
29Das Geheimnisprinzip
Wenn man die Motorhaube eines neueren Autos
öffnet, dann sieht man recht wenig vom Motor.
Einblick in das eigentliche Geschehen im Motor
hat man nicht, wesentliche Teile des Motors
werden sogar durch Abdeckungen schwer zugänglich
gemacht. Man kann allenfalls überprüfen, ob man
genug Öl oder Bremsflüssigkeit hat. Diese
Vorgehensweise, den Motor eines Autos nur noch
für Spezialisten zugänglich zu machen, wird ganz
bewusst von den Autobauern gewählt. Ein Motor ist
heutzutage so kompliziert, dass Laien keine
Veränderungen daran vornehmen sollen.
Beim Autobau wird somit - zumindest in bestimmten
Bereichen - das Geheimnisprinzip angewandt.
Bestimmte Eigenschaften des Motors können nur
über speziell hierfür vorgesehene Schnittstellen
ermittelt werden. So kann der aktuelle Ölstand
nur an einem hierfür vorgesehenen Messstab
abgelesen werden. Änderungen am aktuellen
Motorzustand können direkt ebenfalls nur an
bestimmten hierfür vorgesehenen Stellen
vorgenommen werden. Motoröl lässt sich nur in die
hierfür vorgesehene Öffnung einfüllen. Alles
weitere über das Innere des Motors bleibt für den
normalen Autofahrer unzugänglich und in diesem
Sinne geheim.
30Fachkonzept - Datenkapselung
Software-Objekte (als Programmeinheiten) werden
so konzipiert, dass Details über den inneren
Aufbau verborgen werden und Änderungen von
Objektzuständen nur über dafür vorgesehene
Methoden erfolgen können. Das Verbergen des
inneren Aufbaus wird realisiert, indem man keinen
direkten Zugriff auf die Attribute zur Verwaltung
der internen Daten eines Objekts ermöglicht. Man
nennt diese Vorgehensweise auch Datenkapselung.
31Zugriffsrechte / Zugriffsmethoden
Um interne Daten kapseln zu können, werden
Zugriffrechte festgelegt. Der Entwickler einer
Klasse hat die Möglichkeit, Attribute und
Methoden einer Klasse als öffentlich oder privat
zu deklarieren. Lesende und schreibende Zugriffe
auf Attribute bzw. Methoden eines Objekts sind
nur möglich, wenn diese öffentlich sind. Private
Attribute bzw. Methoden können dagegen nur bei
der Implementierung der betreffenden Klasse
benutzt werden.
Im Klassendiagramm werden die Zugriffsrechte auf
die Attribute und Methoden mit Hilfe der Symbole
(für öffentlich) und - (für privat) festgelegt.
Verfolgt man die Strategie, alle Attribute als
privat zu deklarieren, so besteht keine
Möglichkeit, direkt schreibend oder lesend auf
Attributwerte zuzugreifen. Um dennoch solche
Zugriffe zu erlauben, werden spezielle
öffentliche Zugriffsmethoden bereitgestellt. Das
Klassendiagramm wird daher um solche
Zugriffsmethoden erweitert.
32Zugriffsrechte in Python
class Ampel(object) def __init__(self)
self.__zustand 'rot' def
schalten(self) if self.__zustand
'rot' self.__zustand 'rotgelb'
elif self.__zustand 'rotgelb'
self.__zustand 'gruen' def
getLampen(self) if self.__zustand
'rot' lampen (True, False, False)
return lampen def
getZustand(self) return self.__zustand
def setZustand(self, z)
self.__zustand z
Ein Attribut wird in Python zu einem privaten
Attribut, wenn der Name mit zwei Unterstrichen
beginnt und nicht mit Unterstrichen endet.
Beginnt der Attributname / Methodenname nicht mit
einem Unterstrich, so ist das Attribut
öffentlich. Entsprechendes gilt für Methoden.
33Datenkapselung in Python
gtgtgt a Ampel() gtgtgt a.__zustand Traceback (most
recent call last) File ...
a.__zustand AttributeError 'Ampel' object has no
attribute '__zustand' gtgtgt a.__dict__ '_Ampel__zus
tand' 'rot' gtgtgt a._Ampel__zustand 'rot'
Wie erwartet kann man auf das private Attribut
__zustand des neu erzeugten Objekts a nicht
zugreifen. Python meldet als Fehler, dass es kein
Attribut __zustand gibt. Der Aufruf a.__dict__
verrät, woran das liegt. Ein Aufruf wie
a.__dict__ listet sämtliche Attribute mit den
zugehörigen Attributwerten des betreffenden
Objekts auf. Interessant ist hier, dass sich das
private Attribut __zustand hinter einem anderen
Namen versteckt. Wenn man weiß, wie der neue Name
- hier _Ampel__zustand - gebildet wird, dann kann
man auf das betreffende Attribut zugreifen. Also
Private Attribute werden in Python mit anderen
Namen versehen, so dass kein direkter Zugriff
möglich ist. Kennt man den Namen, hinter dem sich
ein privates Attribut verbirgt, so kann man
durchaus auf dieses Attribut zugreifen. Python
liefert also keinen echten Zugriffsschutz.
34Datenkapselung in Python
gtgtgt a Ampel() gtgtgt a.__zustand Traceback (most
recent call last) File ...
a.__zustand AttributeError 'Ampel' object has no
attribute '__zustand' gtgtgt a.__dict__ '_Ampel__zus
tand' 'rot' gtgtgt a._Ampel__zustand 'rot' gtgtgt
a.__zustand 'gruen' gtgtgt a.__zustand 'gruen' gtgtgt
a.__dict__ '_Ampel__zustand' 'rot',
'__zustand' 'gruen'
Ein erster Zugriff auf das private Attribut
__zustand scheitert. Dann aber ist es - entgegen
aller Zugriffslogik - scheinbar möglich, dem
privaten Attribut __zustand einen Wert
zuzuweisen. Der Aufruf a.__dict__ erklärt erst,
was hier passiert ist. Neben dem privaten
Attribut __zustand, das sich hinter dem neuen
Namen _Ampel__zustand versteckt, gibt es noch
öffentliches Attribut __zustand, auf das man
direkt zugreifen kann.
35Datenkapselung in Python
class Ampel(object) __slots__
('__zustand') def __init__(self)
self.__zustand 'rot' ... wie bisher ...
Mit dem Attribut __slots__ wird festgelegt,
welche Attribute ein Objekt der betreffenden
Klasse haben darf.
gtgtgt a.__zustand Traceback (most recent call
last) File ... a.__zustand AttributeError
'Ampel' object has no attribute '__zustand' gtgtgt
a.__zustand 'gruen' Traceback (most recent call
last) File ... a.__zustand
'gruen' AttributeError 'Ampel' object has no
attribute '__zustand'
36Datenkapselung in Python
Wir werden im Folgenden bei der Implementierung
von Klassen in Python keine Attribute und
Methoden als privat deklarieren. Alle Attribute
und Methoden sind daher direkt zugänglich.
Allerdings werden wir von dem direkten Zugriff in
der Regel keinen Gebrauch machen. Nur in
begründeten Sonderfällen (wie z.B. zum schnellen
Testen) werden wir von dieser Vereinbarung
abweichen.
37Verwendung einer Klasse
Wir werden uns hier mit folgender Frage
beschäftigen Welche Information benötigt man
über eine Klasse, um sie als Baustein zur
Erzeugung und Aktivierung von Software-Objekten
benutzen zu können?
from ampel import Ampel a1 Ampel('rot') a2
Ampel('gruen') print('Ampel 1',
a1.getLampen()) print('Ampel 2',
a2.getLampen()) print() while a1.getZustand() !
'gelb' a1.schalten() a2.schalten()
print('Ampel 1', a1.getLampen())
print('Ampel 2', a2.getLampen()) print()
Nutzung einer Klasse
Aufgabe Was muss der Nutzer alles über die
Klasse Ampel wissen, um ein solches Testprogramm
schreiben zu können?
38Verwendung einer Klasse
Der Entwickler der Klasse Kartenstapel
veröffentlicht das folgende Klassendiagramm
Klassendiagramm
Aufgabe Welche Informationen über die Klasse
Ampel findet man hier? Welche zur Nutzung der
Klasse benötigten Informationen sind hier nicht
dokumentiert.
39Verwendung einer Klasse
class Ampel(object) def __init__(self,
anfangszustand) nachher Ein Objekt
der Klasse Ampel ist erzeugt. Der Wert von
zustand ist gesetzt. self.zustand
anfangszustand def setZustand(self, z)
vorher Der Wert des Attributs
zustand beschreibt eine Ampelphase.
nachher Dem Attribut zustand ist der
Wert des übergebenen Parameters z zugewiesen.
def getZustand(self) Die Funktion
ändert den Objektzustand nicht. Die
Funktion liefert als Ergebnis den Wert von
zustand zurück. def schalten(self)
vorher Der Wert des Attributs zustand
beschreibt eine Ampelphase. nachher
Der Wert des Attributs zustand beschreibt
die nächste Phase gemäß des üblichen
Ampelzyklus "rot -gt rotgelb gt gruen -gt gelb -gt
rot". def getLampen(self) Die
Funktion ändert den Objektzustand nicht.
Die Funktion liefert als Ergebnis ein Tripel
aus Wahrheitswerten, die den zur Phase
passenden Lampenzustand in der Reihenfolge
(Lampe-rot, Lampe-gelb, Lampe-grün) beschreibt.
Schnittstellenbeschreibung
40Schnittstellen
Die Schnittstelle einer Klasse liefert alle
Informationen, die man benötigt, um die Klasse
benutzen zu können. Hierzu gehört eine genaue
Beschreibung aller öffentlichen Attribute und
Methoden der Klasse. Für jedes Attribut benötigt
man den erwarteten Datentyp, für jede Methode die
Signatur (d. h. die genaue Festlegung der
Parametertypen und bei Funktionen des
Rückgabetyps) und eine Verhaltensbeschreibung.
class Ampel(object) def __init__(self)
nachher Ein Objekt der Klasse
Ampel ist erzeugt. Der Wert des
Attributs zustand wird auf den übergebenen
Parameter gesetzt. def setZustand(self,
z) vorher Der Wert des
Attributs zustand beschreibt eine Ampelphase.
nachher Dem Attribut zustand ist
der Wert des übergebenen Parameters z
zugewiesen.
41Modulimport in Python
Baustein importieren from ampel import Ampel
Objekt erzeugen a Ampel('rot') Objekt in
Aktion print(a.getLampen()) a.schalten() print(a.g
etLampen()) while a.getZustand() ! 'rot'
a.schalten() print(a.getLampen())
Baustein importieren import ampel Objekt
erzeugen a ampel.Ampel('rot') Objekt in
Aktion print(a.getLampen()) a.schalten() print(a.g
etLampen()) while a.getZustand() ! 'rot'
a.schalten() print(a.getLampen())
Baustein importieren from ampel import
Objekt erzeugen a Ampel('rot') Objekt in
Aktion ...
Der Name Ampel wird in den aktuellen Namensraum
übernommen.
42Übungen
Aufgabe 1 Es kommt des öfteren vor, dass eine
Implementierung einer Klasse ausgetauscht werden
soll. Wir spielen das im Folgenden einmal
durch. (a) Ergänze die folgende Implementierung
der Klasse Ampel so, dass die Methoden genau
dasselbe Verhalten zeigen wie in der
Schnittstellenbeschreibung zur Klasse (siehe
Folie 39). (b) Warum sollte man keine Zugriffe
auf Attribute benutzen? Begründe mit dem
Austausch einer Implementierung.
class Ampel(object) def __init__(self,
anfangszustand) self.lampeRot ...
self.lampeGelb ...
self.lampeGruen ... def schalten(self)
... def getLampen(self)
return ... def getZustand(self)
return ... def setZustand(self, z)
...
43Anwendung Ampel mit GUI
Ziel ist es, eine einfache grafische
Benutzeroberfläche zu erzeugen, von der aus ein
Objekt der Klasse Ampel aktiviert werden kann.
Die GUI soll dabei ganz einfach gestaltet
sein. Wir benutzen eine dokumentierte
Implementierung der Klasse Ampel, die sich in der
Datei ampel.py befindet. Der Quelltext zeigt eine
einfache Möglichkeit, ein Ampel-Objekt mit einer
GUI zu verknüpfen. Wir werden diese
Verknüpfungsproblematik in einem der folgenden
Abschnitte noch einmal aufgreifen und allgemeiner
lösen.
44Anwendung Ampel mit GUI
Aufgabe 1 (a) Mache dich mit dem Quelltext
vertraut (siehe inf-schule). Ergänze die
Ereignisverarbeitungsprozedur und teste das
Programm. (b) Ergänze das Programm so, dass zwei
Ampeln simuliert werden.
-------------------------------------------------
-------------- Datenmodell --------------------
------------------------------------------- from
ampel import Ampel ampel Ampel('rot') ---------
--------------------------------------------------
---- GUI --------------------------------------
------------------------- def anzeigeAktualisiere
n(lampeRot, lampeGelb, lampeGruen) def
buttonWeiterClick() Verarbeitung der
Daten ... Ampel weiter schalten
Aktualisierung der Anzeige ...
Lampenzustand abfragen und anzeigen from tkinter
import Erzeugung des Fensters
45Teil 3
Beziehungen zwischen Objekten
46Steuerung mehrerer Ampeln
Ziel ist es, komplexere Ampelsysteme, die aus
mehreren Ampeln bestehen, mit Hilfe von
Softwareobjekten zu simulieren. Dabei soll das
Zusammenspiel der Objekte im Vordergrund stehen.
47Ampeln kopieren
Wir betrachten ein System aus zwei Ampeln, die im
Gegentakt schalten sollen.
gtgtgt a1 Ampel(rot') gtgtgt a2 a1 gtgtgt
a2.setZustand('gruen') gtgtgt a1.getZustand() ? gtgtgt
a2.getZustand() ? gtgtgt a1.schalten() gtgtgt
a2.schalten() gtgtgt a1.getZustand() ? gtgtgt
a2.getZustand() ?
Aufgabe (a) Stelle Vermutungen auf, was anstelle
der Fragezeichen jeweils steht. Teste, ob deine
Vermutungen stimmen. (b) Kannst du die
Ergebnisse erklären? (c) Wie muss man vorgehen,
wenn man unabhängig voneinander arbeitende Ampeln
simulieren möchte?
48Identität von Objekten
(Daten-) Objekte haben - analog zu Objekten
unserer Lebenswelt - ebenfalls eine Identität.
Zur eindeutigen Identifizierung werden sie mit
Identitätsnummern versehen. Verschiedene Objekte
unterscheiden sich in ihrer Identitätsnummer. Sie
können aber durchaus denselben Objektzustand
haben. Ein Objekt behält während seiner
Lebensdauer immer die einmal vergebene
Identitätsnummer. Auch wenn sich der Zustand des
Objekts verändert, so bleibt doch die
Identitätsnummer des Objekts bestehen. Häufig
verwendet hierzu man eine Adresse im Speicher des
Rechners als Identitätsnummer. Die
Identitätsnummer eines Objekts zeigt dann auf den
Speicherbereich, in dem die Daten des Objekts
abgelegt sind. Diese Identifikation von Objekten
durch eine Lokalisierung im Speicher setzt
natürlich voraus, dass Objekte im Speicher nicht
hin und her wandern, sondern dass der einmal
zugeteilte Speicherbereich während der
Lebensdauer eines Objekts bestehen bleibt. Wir
gehen im Folgenden von dieser Vorstellung aus.
gtgtgt a1 Ampel(rot') gtgtgt a1 lt__main__.Ampel
object at 0x013.gt gtgtgt id(a1) 20311472 gtgtgt
hex(20311472) '0x135edb0'
gtgtgt a1 Ampel(rot') gtgtgt id(a1) 20311472 gtgtgt a2
a1 gtgtgt id(a2) 20311472
gtgtgt a1 Ampel(rot') gtgtgt id(a1) 20311472 gtgtgt a2
Ampel(rot') gtgtgt id(a2) 20312048
49Zeiger / Referenzen
Eine Variable ist ein Name, der (in der Regel)
mit einem Objekt verknüpft ist. Wenn eine
Variable ein (Daten-) Objekt verwaltet, dann
verwaltet es die Speicheradresse (bzw.
Identitäsnummer) dieses Objekts. Da die
Speicheradresse auf das Objekt zeigt bzw. das
Objekt referenziert, nennt man eine solche
Adresse auch Zeiger bzw. Referenz und die
Variable zur Verwaltung der Adresse
Zeigervariable bzw. Referenzvariable.
50Zuweisungen bei Zeigervariablen
gtgtgt a1 Ampel(rot') gtgtgt id(a1) 20311472 gtgtgt a2
Ampel(rot') gtgtgt id(a2) 20312048
a1 Ampel(rot')
a2 a1
gtgtgt a1 Ampel(rot') gtgtgt id(a1) 20311472 gtgtgt a2
a1 gtgtgt id(a2) 20311472
a1.schalten()
a2 Ampel(rot')
51Objekte in Python
a1 'rot'
a1 Ampel(rot')
w2 w1
a2 a1
a1 'rotgelb'
a1.schalten()
Veränderbares Objekt
Python unterscheidet zwischen veränder-baren und
unveränderbaren Objekten.
Unveränder-bares Objekt
52Ampeln steuern
Der Verkehr in einer Straßenkreuzung soll mit
vier Ampeln geregelt werden. Ziel ist es, ein
Ampelsystem, das aus mehreren Ampeln besteht, mit
Hilfe von Softwareobjekten zu simulieren. Dabei
soll das Zusammenspiel der Objekte im Vordergrund
stehen.
53Ampeln steuern Version 0
Aufgabe Betrachte das Testprogramm. Welche
Ausgaben werden hier erzeugt? Warum sollte die im
Testprogramm erzeugte Objektsituation in der
Wirklichkeit nicht auftreten?
from ampel import ampelNS Ampel('rot') ampelOW
Ampel('gruen') ampelSN Ampel('rot') ampelWO
Ampel('gruen') print(ampelNS.getLampen()) print(
ampelOW.getLampen()) print(ampelSN.getLampen()) pr
int(ampelWO.getLampen()) print() ampelNS.schalten(
) ampelSN.schalten() ampelNS.schalten() ampelSN.sc
halten() print(ampelNS.getLampen()) print(ampelOW.
getLampen()) print(ampelSN.getLampen()) print(ampe
lWO.getLampen()) print()
54Ampeln steuern Version 1
Bei der Realisierung des Ampelsystems muss
verhindert werden, dass alle Ampeln auf "grün"
stehen. Ein zusätzliches Objekt ampelmanager
einer neuen Klasse Ampelmanager soll diese
Aufgabe übernehmen. Damit das Objekt ampelmanager
die Ampel-Objekte gezielt aktivieren kann, wird
das Objekt ampelmanager mit Zeigern auf die
Ampel-Objekte versehen.
55Ampeln steuern Version 1
class Ampel(object) ... wie bisher class
AmpelManager(object) def __init__(self)
self.ampel1 None self.ampel2
None self.ampel3 None
self.ampel4 None def schalteAmpeln(self)
self.ampel1.schalten()
self.ampel2.schalten() self.ampel3.schalte
n() self.ampel4.schalten()
56Ampeln steuern Version 1
Aufgabe Die Anweisungen im Testprogramm sind
durchnummeriert. Welche dieser Anweisungen werden
benutzt, um die Objekte zu erzeugen und zu
verbinden? Gib hierzu genau an, wie die oben
gezeigte Objektkonstellation hier Schritt für
Schritt erzeugt wird.
ampelNS Ampel('rot') 1 ampelOW
Ampel('gruen') 2 ampelSN
Ampel('rot') 3 ampelWO
Ampel('gruen') 4 ampelmanager
AmpelManager() 5 ampelmanager.ampel1
ampelNS 6 ampelmanager.ampel2 ampelOW
7 ampelmanager.ampel3 ampelSN
8 ampelmanager.ampel4 ampelWO 9
57Ampeln steuern Version 1
Aufgabe (a) Schaue dir den Konstruktor der Klasse
Ampelmanager genau an. Wozu dienen die Parameter
a1, ..., a4? (b) Entwickle ein Testprogramm zur
veränderten Implementierung.
-------------------------------------------------
----------------- Ampel ------------------------
------------------------------------------ class
Ampel(object) ... wie bisher ------------
--------------------------------------------------
---- Ampelmanager -----------------------------
------------------------------------- class
AmpelManager(object) def __init__(self, a1,
a2, a3, a4) self.ampel1 a1
self.ampel2 a2 self.ampel3 a3
self.ampel4 a4 def schalteAmpeln(self)
self.ampel1.schalten()
self.ampel2.schalten() self.ampel3.schalte
n() self.ampel4.schalten()
58Ampeln steuern Version 2
Wenn ein Ampelsystem aus vielen Ampeln besteht,
dann ist es meist günstiger, wenn das Objekt
ampelmanager die Gesamtheit aller Ampeln mit
Hilfe einer Liste verwaltet.
59Ampeln steuern Version 2
Test ampelNS Ampel('rot') ampelOW
Ampel('gruen') ampelSN Ampel('rot') ampelWO
Ampel('gruen') ampelnKreuzung ampelNS,
ampelOW, ampelSN, ampelWO ampelmanager
AmpelManager() ampelmanager.setListeAmpeln(ampelnK
reuzung) for ampel in ampelmanager.getListeAmpeln(
) print(ampel.getLampen()) print() for i in
range(8) ampelmanager.schalteAmpeln()
for ampel in ampelmanager.getListeAmpeln()
print(ampel.getLampen()) print()
class AmpelManager(object) def
__init__(self) self.listeAmpeln None
def setListeAmpeln(self, pListeAmpeln)
self.listeAmpeln pListeAmpeln def
getListeAmpeln(self) return
self.listeAmpeln def
schalteAmpeln(self) ... ...
Aufgabe Die Implementierung der Methode
schalteAmpeln der Klasse Ampelmanager ist noch
nicht ganz fertig. Ergänze die fehlenden
Anweisungen und teste das gesamte Programm.
60Ampeln steuern Version 3
Die Ampel-Objekte wurden bisher unabhängig vom
Ampelmanager-Objekt erzeugt. In der folgenden
Implementierung wird das jetzt anders gemacht.
class AmpelManager(object) def
__init__(self) self.listeAmpeln
def erzeugeAmpeln(self) ampelNS
Ampel('rot') ampelOW Ampel('gruen')
ampelSN Ampel('rot') ampelWO
Ampel('gruen') self.listeAmpeln
ampelNS, def getListeAmpeln(self)
return self.listeAmpeln
def schalteAmpeln(self) for ampel
in self.listeAmpeln
ampel.schalten() ...
Test ampelmanager AmpelManager() ampelmanage
r.erzeugeAmpeln() for ampel in ampelmanager.getLis
teAmpeln() print(ampel.getLampen()) print() f
or i in range(8) ampelmanager.schalteAmpeln()
for ampel in ampelmanager.getListeAmpeln()
print(ampel.getLampen()) print()
61Ampeln steuern Version 3
Aufgabe Wie wird hier die Objektkonstellation
erzeugt?
class AmpelManager(object) def
__init__(self) self.listeAmpeln
def erzeugeAmpel(self, anfangszustand)
ampel Ampel(anfangszustand)
self.listeAmpeln self.listeAmpeln ampel
def getListeAmpeln(self) return
self.listeAmpeln def
schalteAmpeln(self) for ampel in
self.listeAmpeln ampel.schalten() ...
Test ampelmanager AmpelManager() ampelmanage
r.erzeugeAmpel('rot') ampelmanager.erzeugeAmpel('g
ruen') ampelmanager.erzeugeAmpel('rot') ampelmanag
er.erzeugeAmpel('gruen')
62Beziehung zwischen Objekten
Unter den vielen Beziehungen zwischen Objekten in
unserer Lebenswelt spielt die kennt-Beziehung
eine wichtige Rolle. Ein "Objekt Peter" kann
einem "Objekt Anna" nur dann eine SMS mir einer
Einladung zu einem Treffen schicken, wenn das
"Objekt Peter" das "Objekt Anna" kennt (genauer
die Telefonnummer des "Objekts Anna"). Ähnlich
verhält es sich in der Welt der Software-Objekte.
Ein Software-Objekt kann nur dann ein anderes
Software-Objekts veranlassen, eine Methode
auszuführen, wenn es dieses andere
Software-Objekt kennt (genauer die
Speicheradresse des anderen Software-Objekts). Um
ein solches In-Beziehung-Setzen von
Software-Objekte zu realisieren, werden
Software-Objekte mit Attributen versehen, mit
deren Hilfe sie Referenzen auf "Beziehungspartner"
verwalten können.
63Fachkonzept - Beziehung
Wenn ein Objekt über einen Zeiger (eine Referenz)
Zugriff auf ein anderes Objekt hat, so liegt eine
(gerichtete) Beziehung zwischen den Objekten vor.
Damit das Ampelmanager-Objekt Zugriff auf die
Ampel-Objekte erhält, wird es mit sogenannten
Referenzattributen versehen. Das
Ampelmanager-Objekt verfügt über Attribute, deren
Werte Referenzen bzw. Zeiger auf die
entsprechenden Ampel-Objekt sind. Zur Erinnerung
Eine Referenz bzw. ein Zeiger ist nichts anderes
als die Adresse, unter der das Objekt im Speicher
zu finden ist.
Objektdiagramm
Klassendiagramm
64Beziehungsmuster
Muster Objekt der Klasse A kennt Objekt der
Klasse B
Muster Objekt der Klasse A erzeugt Objekt der
Klasse B
Muster Objekt der Klasse A kennt Objekt der
Klasse B und umgekehrt
Muster Objekt der Klasse A kennt mehrere
Objekte der Klasse B
65Hat-Beziehung
Eine besondere Form der Beziehung liegt vor, wenn
ein Objekt ein anderes erzeugt (und wieder
vernichtet) und somit die Kontrolle über die
Existenz des verwalteten Objekts hat. Wenn
beispielsweise das Objekt a eine Methode
ausführt, innerhalb derer ein Aufruf zur
Erzeugung eines Objekts b vorkommt, so liegt die
im Objektdiagramm gezeigte Situation vor
Im Klassendiagramm wird diese Form der
Abhängigkeit wie folgt beschrieben
66Interaktion zwischen Objekten
Objekte können (in aller Regel) bestimmte
Operationen mit den von ihnen verwalteten Daten
ausführen. Die Ausführung einer Operationen wird
als Dienst anderen Objekten zur Verfügung
gestellt. Andere Objekte können den zur Verfügung
gestellten Dienst dann nutzen. Hier wird also die
Anbieter-Nutzer-Sichtweise benutzt. Wenn ein
Objekt den Dienst eines anderen Objekt nutzen
will, dann schickt es ihm eine Nachricht. Das
Senden einer Nachricht bedeutet, ein Objekt zu
veranlassen, eine seiner als Dienste zur
Verfügung gestellten Operationen auszuführen. Das
Versenden von Nachrichten wird als Interaktion
zwischen Objekten gedeutet. Voraussetzung für
eine Interaktion zwischen Objekten ist, dass
diese miteinander in Beziehung stehen.
67Übungen
Aufgabe 1 Betrachte ein Ampelsystem, das aus
einer Auto- und einer Fußgängerampel
besteht. Ändere das bisher betrachtete
Ampelsystem so ab, dass es die betrachtete
Situation sinnvoll simuliert.
68Übungen
Aufgabe 2 Eine Ampel hat drei Lampen, die
einzeln an- und ausgeschaltet werden können.
Modelliere diese Situation mit Software-Objekten.
(a) Erstelle zunächst ein Objektdiagramm, in dem
die Objekte und ihre Beziehungen dargestellt
werden. (b) Entwickle anschließend ein
Klassendiagramm zur Situation. (c) Implementiere
die Klassen und teste sie mit einem Testprogramm.
69Teil 4
Vererbung
70Gemeinsamkeiten bei Ampeln
Auto- und Fußgängerampeln weisen eine Reihe von
Gemeinsamkeiten auf. So haben beide einen
bestimmten Zustand und können weitergeschaltet
werden. Solche Gemeinsamkeiten spiegeln sich z.T.
auch in den Klassendiagrammen wider.
Zielsetzung Wir werden eine allgemeine Klasse
Ampel konzipieren und Auto- bzw. Fußgängerampeln
als spezielle Ampeln auffassen.
71Gemeinsamkeiten bei Ampeln
Auto- und Fußgängerampeln weisen eine Reihe von
Gemeinsamkeiten auf. So haben beide einen
bestimmten Zustand und können weitergeschaltet
werden. Solche Gemeinsamkeiten spiegeln sich z.T.
auch in den Klassendiagrammen wider.
72Gemeinsamkeiten bei Ampeln
class AmpelAuto(object) def __init__(self,
anfangszustand) self.zustand
anfangszustand def setZustand(self, z)
self.zustand z def getZustand(self)
return self.zustand def
schalten(self) if self.zustand
'rot' self.zustand 'rotgelb'
elif self.zustand 'rotgelb'
def getLampen(self) if self.zustand
'rot' lampen (True, False, False)
return lampen
class AmpelFussgaenger(object) def
__init__(self, anfangszustand)
self.zustand anfangszustand def
setZustand(self, z) self.zustand z
def getZustand(self) return
self.zustand def schalten(self) if
self.zustand 'rot' self.zustand
'gruen' elif self.zustand 'gruen'
self.zustand 'rot' def
getLampen(self) if self.zustand
'rot' lampen (True, False)
return lampen
Codeduplizierung
73Eine allgemeine Ampelklasse
Auto- und Fußgängerampeln weisen eine Reihe von
Gemeinsamkeiten auf. Beim Weiterschhalten
durchlaufen beide zyklisch eine bestimmte Folge
von Zuständen.
74Eine allgemeine Ampelklasse
class Ampel(object) def __init__(self,
pListeZustaende) self.listeZustaende
pListeZustaende self.indexAktuellerZustand
0 def schalten(self) if
self.indexAktuellerZustand lt len(self.listeZustaen
de)-1 self.indexAktuellerZustand
self.indexAktuellerZustand 1 else
self.indexAktuellerZustand 0 def
getZustand(self) return
self.listeZustaendeself.indexAktuellerZustand
def setZustand(self, z)
self.indexAktuellerZustand self.listeZustaende.i
ndex(z)
Aufgabe Simuliere mit dieser Klasse das
Verhalten einer Autoampel / Fußgängerampel.
75Spezielle Ampelklassen
Aus der allgemeinen Ampel-Klasse kann man durch
eine Erweiterung Klassen für Auto- und
Fußgängerampeln gewinnen.
Die Klassen AmpelAuto und AmpelFussgaenger sollen
dabei sämtliche Attribute und Methoden der
Basisklasse Ampel übernehmen (man sagt auch
erben) und zusätzlich die im Klassendiagramm
aufgeführten neuen Methoden (die
Konstruktormethode und die Methode getLampen)
vorsehen.
76Implementierung
Hier eine Implementierung der beiden Klassen
Ampel und AmpelAuto. Beachte den
Erweiterungshinweis AmpelAuto(Ampel) bei der
Deklaration der Klasse AmpelAuto.
class Ampel(object) wie oben class
AmpelAuto(Ampel) def __init__(self,
anfangszustand) self.listeZustaende
'rot', 'rotgelb', 'gruen', 'gelb'
self.indexAktuellerZustand self.listeZustaende.i
ndex(anfangszustand) def
getLampen(self) zustand
self.listeZustaendeself.indexAktuellerZustand
if zustand 'rot' lampen
(True, False, False) elif zustand
'rotgelb' lampen (True, True,
False) elif zustand 'gruen'
lampen (False, False, True) elif
zustand 'gelb' lampen (False,
True, False) return lampen
77Implementierung
Aufgabe 1 Führe das Programm selbst aus. Kannst
du das Verhalten erklären?
Test a Ampel('rot', 'rotgelb', 'gruen',
'gelb') a.setZustand('rot') print(a.getZustand())
print() for i in range(4) a.schalten()
print(a.getZustand()) print()
Aufgabe 2 Entwickle analog eine Klasse
AmpelFussgaenger und teste sie.
78Implementierung
Aufgabe 3 Vergleiche die Implementierungen.
Worin unterscheiden sie sich?
class Ampel(object) def __init__(self,
pListeZustaende) self.listeZustaende
pListeZustaende self.indexAktuellerZustand
0 ... class AmpelAuto(Ampel) def
__init__(self, anfangszustand)
self.listeZustaende 'rot', 'rotgelb', 'gruen',
'gelb' self.indexAktuellerZustand
self.listeZustaende.index(a
nfangszustand) ...
class Ampel(object) def __init__(self,
pListeZustaende) self.listeZustaende
pListeZustaende self.indexAktuellerZustand
0 ... class AmpelAuto(Ampel) def
__init__(self, anfangszustand)
Ampel.__init__(self, 'rot', 'rotgelb', 'gruen',
'gelb') self.indexAktuellerZustand
self.listeZustaende.index(
anfangszustand) ...
79Fachkonzept - Vererbung
Vererbung beschreibt die Vorgehensweise, eine
neue Klasse als Erweiterung einer bereits
bestehenden Klasse (oder mehrerer bereits
bestehender Klassen) zu entwickeln. Die neue
Klasse wird auch Subklasse genannt. Die
bestehende Klasse wird Basisklasse oder
Superklasse genannt.
Übernehmen, ergänzen und überschreiben Beim
Vererben übernimmt die Subklasse die Attribute
und Methoden der Basisklasse. Eine übernommene
Methode kann dabei überschrieben (d. h. neu
definiert) werden. Die Subklasse kann dann noch
zusätzliche Attribute und Methoden ergänzen.
80Fachkonzept - Vererbung
Vorteile von Vererbung Man kann Methoden der
Basisklasse(n) nutzen. Nachteile von
Vererbung Eine Subklasse ist keine autonome
Einheit, die ohne die Basisklasse(n) verwendet
werden kann.
Hinweis Wir werden Vererbung im Folgenden bei
der Entwicklung objektorientierter Modelle eher
vermeiden.
81Teil 5
Miniwelt und Datenmodell
82Eine Miniwelt mit Ampeln
Wer komplexere Software-Systeme entwickeln
möchte, muss zunächst einmal die Daten des
Gegenstandsbereichs erfassen. Das macht man am
besten mit einem sogenannten Datenmodell.
Ziel ist es, ein Programm zu entwickeln, mit dem
man der Steuerung der Ampeln simulieren kann. Wir
werden uns hier auf die Entwicklung eines
objektorientierten Modells konzentrieren. Mit
diesem Modell versuchen wir, die Miniwelt
möglichst strukturgetreu zu erfassen.
83Eine Miniwelt mit Ampeln
/1/ Die Ampeln in Nord-Süd- bzw.
Süd-Nord-Richtung sollen gleich geschaltet
werden. Entsprechendes soll für die Ampeln in
Ost-West- bzw. West-Ost-Richtung gelten. /2/ Wenn
die Ampeln in der einen Ausrichtung in der Phase
gelb, grün oder rotgelb sind, dann sollen die
Ampeln in der anderen Ausrichtung in der Phase
rot sein. /3/ Die grün-Phase soll 3-mal so lang
dauern wie die gelb- bzw. rotgelb-Phase. /4/ Die
Fußgängerampeln sollen nur dann in der grün-Phase
sein, wenn der Autoverkehr in derselben Richtung
auch in der grün-Phase ist.
84Eine Miniwelt mit Ampeln
Aufgabe Entwickle einen Schaltplan für dieses
Ampelsystem. T bzw. F steht hier abkürzend für
True bzw. False und soll angeben, ob die
entsprechenden Ampeln geschaltet werden oder
nicht.
85Objekte und ihre Zuständigkeiten
Aufgabe Welche Objekte sollen bei der
Modellierung der Miniwelt eingeführt werden?
86Objekte konzipieren
Aufgabe (a) Entwickle ein Objektdiagramm, so
dass alle Daten von einem passenden Objekt
verwaltet werden. Führe hier geeignete Attribute
ein und ordne ihnen die in der Miniwelt
vorkommenden Daten als momentane Attributwerte
zu. (b) Berücksichtige im Objektdiagramm auch die
Referenzattribute, mit denen die Beziehungen
zwischen den Objekten realisiert werden sollen.
87Die Klasse Ampel
Aufgabe Beschreibe eine Klasse Ampel /
Fußgängerampel mit einem Klassendiagramm.
88Die Klasse Zaehler
Aufgabe Mit einem Zähler soll die aktuelle
Position im Schaltplan verwaltet werden. Beachte,
dass hier ein Zähler benötigt wird, der nach der
9 wieder auf 0 springt. Entwickle und beschreibe
eine Klasse Zaehler, die es erlaubt, einfache
Zähleroperationen (wie weiterzaehlen und
nullsetzen) durchzuführen.
89Die Klasse Schaltplanverwalter
- Aufgabe
- Ein Objekt der Klasse Schaltplanverwalter hat
eine Reihe von Zuständigkeiten - ... Verwaltung der Schaltpläne für die Ampeln
- ... Erzeugung und Verwaltung eines
Zaehler-Objekts - ... Bestimmung und Auslieferung der Schaltsignale
zum aktuellen Zählerstand mit anschließender
Aktualisierung des Zählers - Entwickle ein Klassendiagramm zur Klasse
Schaltplanverwalter, das die beschriebenen
Zuständigkeiten umsetzt.
90Die Klasse Ampelmanager
- Aufgabe
- Ein Objekt der Klasse Ampelmanagerhat eine Reihe
von Zuständigkeiten - ... Verwaltung der Ampel-Objekte
- ... Verwaltung des Schaltplanverwalter-Objekts
- ... Schalten der Ampeln
- Entwickle ein Klassendiagramm zur Klasse
Schaltplanverwalter, das die beschriebenen
Zuständigkeiten umsetzt.
91Die Klasse Ampelmanager
92Implementierung des Datenmodells
Aufgabe Version A Entwickle passend zum
Datenmodell eine Implementierung zum Ampelsystem.
Entwickle auch ein einfaches Testprogramm, mit
dem man alle Funktionalitäten des Datenmodells
überprüfen kann.
Aufgabe Version B Analysiere die vorgegebene
Implementierung des Datenmodells. Kannst du alle
Bestandteile erklären? Entwickle ein einfaches
Testprogramm, mit dem man alle Funktionalitäten
des Datenmodells überprüfen kann.
93Miniwelt und Datenmodell
Mit Miniwelt bezeichnen wir den Weltausschnitt /
Gegenstandsbereich, der dem zu entwickelnden
Programm (Software-System) zu Grunde liegt.
94Miniwelt und Datenmodell
Ein Modell ist eine vereinfachende Darstellung
einer Miniwelt, die (für einen bestimmten Zweck
ausgewählte) Teilaspekte der Miniwelt
strukturgetreu beschreibt. Aufgabe eines
objektorientiertes Datenmodells ist es, die
Struktur der Miniwelt mit Hilfe von Objekten und
deren Beziehungen zu beschreiben.
95Exkurs - UML
UML steht für "uniform modeling language" und ist
eine normierte Bildsprache, mit der man
objektorientierte Modelle beschreiben kann.
Objektdiagramm
96Exkurs - UML
UML steht für "uniform modeling language" und ist
eine normierte Bildsprache, mit der man
objektorientierte Modelle beschreiben kann.
Klassendiagramm
97Exkurs - UML
UML steht für "uniform modeling language" und ist
eine normierte Bildsprache, mit der man
objektorientierte Modelle beschreiben kann.
Sequenzdiagramm
98Exkurs - UML
UML steht für "uniform modeling language" und ist
eine normierte Bildsprache, mit der man
objektorientierte Modelle beschreiben kann.
Zustandsdiagramm
99Teil 6
Datenmodell und GUI
100Datenmodell und GUI
Wir betrachten hier die Verknüpfung einer
grafischen Benutzeroberfläche mit einem
Datenmodell.
101Verwendung von GUI-Objekten
-------------------------------------------------
-- Erzeugung des Datenmodell-Objekts ----------
----------------------------------------- from
ampel import Ampel ampel Ampel('rot') ---------
------------------------------------------
GUI ---------------------------------------------
------ from tkinter import
Ereignisverarbeitung def buttonWeiterClick()
Verarbeitung der Daten ampel.schalten()
Aktualisierung der Anzeige (lampeRot,
lampeGelb, lampeGruen)
ampel.getLampen() if lampeRot
canvas.itemconfigure(id_rot,
fillrotAn)
Erzeugung des Fensters fenster
Tk() fenster.title("Ampel") fenster.geometry("400x
300") Zeichenfläche canvas Canvas(masterfenst
er) canvas.place(x0, y0, width400,)
Rot-Licht id_rot canvas.create_oval(252, 122,
260,
130, fillgrau) Button zum
Weiterschalten buttonWeiter Button(masterfenste
r, text"weiter",
commandbuttonWeiterClick) buttonWe
iter.place(x150, y270, ) Ereignisschleife
starten fenster.mainloop()
Datenmodell-Objekt
GUI-Objekt
GUI-Objekt
102Objektkonstellation
-------------------------------------------------
-- Erzeugung des Datenmodell-Objekts ----------
----------------------------------------- ampel
Ampel('rot') ----------------------------------
----------------- GUI -------------------------
-------------------------- Erzeugung des
Fensters fenster Tk() Zeichenfläche canvas
Canvas(masterfenster) Button zum
Weiterschalten buttonWeiter Button(masterfenste
r, text"weiter",
commandbuttonWeiterClick)
Datenmodell-Objekt
GUI-Objekte
103Objektkonstellation mit GUI-Verwalter
GUI-Objekte-Verwalter
GUI-Objekte
Datenmodell-Objekt
104Eine GUI-Klasse
class GUI(object) def __init__(self,
datenmodell) Referenzattribute zum
Datenmodell self.ampel datenmodell0
Erzeugung des Fensters
self.fenster Tk()
Zeichenfläche self.canvas
Canvas(masterself.fenster)
self.canvas.place(x0, y0, width400,
height300) Rot-Licht
self.id_rot self.canvas.create_oval(252, 122,
260, 130, fillgrau)
Aktualisierung der Anzeige
self.anzeigeAktualisieren() Button zum
Weiterschalten self.buttonWeiter
Button(masterself.fenster,
text"weiter",
commandself.buttonWeiterClick)
self.buttonWeiter.place(x150, y270,
width100, height20)
Kennt Datenmodell
Hat GUI-Objekt
105Eine GUI-Klasse
----------------------------------------------
-------------------------------- Erzeugung der
Objekte -----------------------------------------
------------------------------------- ampel
Ampel('rot') datenmodell ampel gui
GUI(datenmodell) gui.fenster.mainloop()
106Datenmodell-GUI-Architektur
Aufgabe Das Software-System benutzt eine
2-Schichten-Architektur Die obere Schicht ist
für die GUI zuständig, die untere