ppt - Informatik

Download Report

Transcript ppt - Informatik

Konzepte der objektorientierten
Programmierung
Klaus Becker
2013
2
Simulation 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.
3
Teil 1
Objekte und Klassen
4
Verwaltung 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.
Rollenspiel zur Simulation
5
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
Rollenspiel zur Simulation
6
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
7
Ein 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 SoftwareObjekt 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.
8
Ein 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.
9
Ampel-Objekt in Aktion
>>>
>>> ampel1 = Ampel()
>>> ampel1.lampeRot
False
>>> ampel1.lampeGelb
False
>>> ampel1.lampeGruen
False
>>> ampel1.setLampen(True, False, False)
>>> ampel1.lampeRot
True
>>> ampel1.lampeGelb
False
>>> ampel1.lampeGruen
False
>>> ampel1.schalten()
>>> ampel1.lampeRot
True
>>> ampel1.lampeGelb
True
>>> ampel1.lampeGruen
False
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.
Ampel-Objekt in Aktion
10
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.
11
Objekte 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.
12
Software-Objekte
Analog zu einer Ampel (als Objekt der realen Welt) soll ein
Software-Objekt ampel konzipiert werden. Das folgende
Ablaufprotokoll verdeutlicht die Arbeitsweise eines SoftwareObjekts 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.
13
Fachkonzept - 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.
Objekt
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.
Attribute Attributwerte
Ausführung
einer Methode
Ein Objekt befindet sich stets in einem
bestimmten Zustand. Der aktuelle
Objektzustand wird durch die aktuellen
Werte der Attribute festgelegt.
Objektdiagramm
14
Fachkonzept - Objekt
Zugriff auf Attribute:
Objekt
objekt.attribut
ampel1.lampeGruen
Aktivierung von Methoden:
objekt.methode
Attribute Attributwerte
ampel1.schalten()
Ausführung
einer Methode
Objektdiagramm
15
Klasse 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.
Schuhe 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.
16
Fachkonzept - 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.
17
Konstruktor / 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.
18
Klassendeklaration in Python
Klassenname
Oberklasse
Schlüsselwort
Einrückung
Attribute
class Ampel(object):
def __init__(self):
self.lampeRot = False
self.lampeGelb = False
self.lampeGruen = False
Doppelpunkt
Konstruktor
Methode
def setLampen(self, startwertLampeRot, …):
self.lampeRot = startwertLampeRot
self.lampeGelb = startwertLampeGelb
self.lampeGruen = startwertLampeGruen
def schalten(self):
…
19
Objekterzeugung in Python
>>> ampel1 = Ampel()
>>> ampel1
<__main__.Ampel object at 0x0136C4B0>
>>> ampel1.__dict__
{'lampeRot': False, 'lampeGelb': False, 'lampeGruen': False}
>>> ampel1 = Ampel()
>>> ampel1
<__main__.Ampel object at 0x0136C4B0>
>>> del ampel1
>>> ampel1
Traceback (most recent call last):
File ...
ampel1
NameError: name 'ampel1' is not defined
>>>
Erzeugung eines Objekts
Inspektion eines Objekts
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.
Übungen
21
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.
>>>
>>>
>>>
?
>>>
?
>>>
>>>
>>>
>>>
?
>>>
?
a1 = Ampel('rot')
a2 = Ampel('gruen')
a1.getLampen()
a2.getLampen()
a1.schalten()
a2.schalten()
a1.setZustand('rot')
a1.getLampen()
a2.getLampen()
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
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 ausund 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.
23
Teil 2
Modularisierung
24
Das 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.
25
Experimente mit Ampel-Objekten
class Ampel(object):
def __init__(self):
self.zustand = 'rot'
self.lampeRot = None
self.lampeGelb = None
self.lampeGruen = None
self.aktualisiereLampen()
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.aktualisiereLampen()
…
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
Es gibt eine Vielzahl von Möglichkeiten, eine
Klasse Ampel zur Simulation einer solchen
Ampel zu konzipieren.
26
Experimente mit Ampel-Objekten
class Ampel(object):
def __init__(self):
self.zustand = 'rot'
self.lampeRot = None
self.lampeGelb = None
self.lampeGruen = None
self.aktualisiereLampen()
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.aktualisiereLampen()
>>> a = Ampel()
>>> a.zustand
'rot'
>>> a.getLampen()
(True, False, False)
>>> a.schalten()
>>> a.zustand
'rotgelb'
>>> a.getLampen()
(True, True, False)
>>> a.zustand = 'gruen'
>>> a.zustand
'gruen'
>>> a.getLampen()
(True, True, False)
Aufgabe:
Führe selbst diesen Dialog aus. Warum ist
dieser Dialog nicht im Sinne des
Ampelsystems? Woran liegt das?
27
Experimente mit Ampel-Objekten
class Ampel(object):
def __init__(self):
self.zustand = 'rot'
self.lampeRot = None
self.lampeGelb = None
self.lampeGruen = None
self.aktualisiereLampen()
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'
>>> a = Ampel()
>>> a.getZustand()
'rot'
>>> a.getLampen()
(True, False, False)
>>> a.schalten()
>>> a.getZustand()
'rotgelb'
>>> a.getLampen()
(True, True, False)
>>> a.setZustand('gruen')
>>> a.getZustand()
'gruen'
>>> a.getLampen()
(False, False, True)
Kein Zugriff auf die Attribute
28
Experimente mit Ampel-Objekten
class Ampel(object):
def __init__(self):
self.zustand = 'rot'
self.lampeRot = None
self.lampeGelb = None
self.lampeGruen = None
self.aktualisiereLampen()
def setZustand(self, z):
self.zustand = z
self.aktualisiereLampen()
def getZustand(self):
return self.zustand
>>> a = Ampel()
>>> a.getZustand()
'rot'
>>> a.getLampen()
(True, False, False)
>>> a.schalten()
>>> a.getZustand()
'rotgelb'
>>> a.getLampen()
(True, True, False)
>>> a.setZustand('gruen')
>>> a.getZustand()
'gruen'
>>> a.getLampen()
(False, False, True)
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?
29
Das 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.
30
Fachkonzept - 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.
31
Zugriffsrechte / 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.
32
Zugriffsrechte 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.
33
Datenkapselung in Python
>>> a = Ampel()
>>> a.__zustand
Traceback (most recent call last):
File ...
a.__zustand
AttributeError: 'Ampel' object has no attribute '__zustand'
>>> a.__dict__
{'_Ampel__zustand': 'rot'}
>>> 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.
34
Datenkapselung in Python
>>> a = Ampel()
>>> a.__zustand
Traceback (most recent call last):
File ...
a.__zustand
AttributeError: 'Ampel' object has no attribute '__zustand'
>>> a.__dict__
{'_Ampel__zustand': 'rot'}
>>> a._Ampel__zustand
'rot'
>>> a.__zustand = 'gruen'
>>> a.__zustand
'gruen'
>>> 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.
Datenkapselung in Python
35
class Ampel(object):
__slots__ = ('__zustand')
Mit dem Attribut __slots__ wird
festgelegt, welche Attribute ein Objekt
der betreffenden Klasse haben darf.
def __init__(self):
self.__zustand = 'rot'
# ... wie bisher ...
>>> a.__zustand
Traceback (most recent call last):
File ...
a.__zustand
AttributeError: 'Ampel' object has no attribute '__zustand'
>>> a.__zustand = 'gruen'
Traceback (most recent call last):
File ...
a.__zustand = 'gruen'
AttributeError: 'Ampel' object has no attribute '__zustand'
36
Datenkapselung 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.
37
Verwendung 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?
38
Verwendung 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.
39
Verwendung einer Klasse
class Ampel(object):
Schnittstellenbeschreibung
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 -> rotgelb > gruen -> gelb -> 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.
Schnittstellen
40
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.
41
Modulimport in Python
# Baustein importieren
import ampel
# Objekt erzeugen
a = ampel.Ampel('rot')
# Objekt in Aktion
print(a.getLampen())
a.schalten()
print(a.getLampen())
while a.getZustand() != 'rot':
a.schalten()
print(a.getLampen())
# Baustein importieren
from ampel import Ampel
# Objekt erzeugen
a = Ampel('rot')
# Objekt in Aktion
print(a.getLampen())
a.schalten()
print(a.getLampen())
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.
Übungen
42
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):
# ...
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.
43
Anwendung – 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.
44
Anwendung – Ampel mit GUI
#--------------------------------------------------------------# Datenmodell
#--------------------------------------------------------------from ampel import Ampel
ampel = Ampel('rot')
#--------------------------------------------------------------# GUI
#--------------------------------------------------------------def anzeigeAktualisieren(lampeRot, lampeGelb,
lampeGruen):
…
def buttonWeiterClick():
# Verarbeitung der Daten
# ... Ampel weiter schalten
# Aktualisierung der Anzeige
# ... Lampenzustand abfragen und anzeigen
from tkinter import *
# Erzeugung des Fensters
…
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.
45
Teil 3
Beziehungen zwischen Objekten
46
Steuerung 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.
Ampeln kopieren
47
Wir betrachten ein System aus zwei Ampeln, die im Gegentakt
schalten sollen.
>>>
>>>
>>>
>>>
?
>>>
?
>>>
>>>
>>>
?
>>>
?
a1 = Ampel(‘rot')
a2 = a1
a2.setZustand('gruen')
a1.getZustand()
a2.getZustand()
a1.schalten()
a2.schalten()
a1.getZustand()
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?
48
Identitä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.
>>> a1 = Ampel(‘rot')
>>> a1
<__main__.Ampel object at 0x013….>
>>> id(a1)
20311472
>>> hex(20311472)
'0x135edb0'
>>> a1 = Ampel(‘rot')
>>> id(a1)
20311472
>>> a2 = Ampel(‘rot')
>>> id(a2)
20312048
>>> a1 = Ampel(‘rot')
>>> id(a1)
20311472
>>> a2 = a1
>>> id(a2)
20311472
49
Zeiger / 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.
50
Zuweisungen bei Zeigervariablen
a1 = Ampel(‘rot')
a2 = a1
a1.schalten()
a2 = Ampel(‘rot')
>>> a1 = Ampel(‘rot')
>>> id(a1)
20311472
>>> a2 = Ampel(‘rot')
>>> id(a2)
20312048
>>> a1 = Ampel(‘rot')
>>> id(a1)
20311472
>>> a2 = a1
>>> id(a2)
20311472
51
Objekte in Python
a1 = Ampel(‘rot')
a1 = 'rot'
a2 = a1
w2 = w1
a1.schalten()
a1 = 'rotgelb'
Veränderbares
Objekt
Python unterscheidet zwischen veränderbaren und unveränderbaren Objekten.
Unveränderbares Objekt
52
Ampeln 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.
NS
OW
WO
SN
53
Ampeln steuern – Version 0
from ampel import *
ampelNS = Ampel('rot')
ampelOW = Ampel('gruen')
ampelSN = Ampel('rot')
ampelWO = Ampel('gruen')
print(ampelNS.getLampen())
print(ampelOW.getLampen())
print(ampelSN.getLampen())
print(ampelWO.getLampen())
print()
ampelNS.schalten()
ampelSN.schalten()
ampelNS.schalten()
ampelSN.schalten()
print(ampelNS.getLampen())
print(ampelOW.getLampen())
print(ampelSN.getLampen())
print(ampelWO.getLampen())
print()
Aufgabe:
Betrachte das
Testprogramm.
Welche Ausgaben
werden hier erzeugt?
Warum sollte die im
Testprogramm
erzeugte
Objektsituation in der
Wirklichkeit nicht
auftreten?
Ampeln steuern – Version 1
54
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.
NS
OW
WO
SN
Ampeln steuern – Version 1
55
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.schalten()
self.ampel4.schalten()
56
Ampeln 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')
ampelOW = Ampel('gruen')
ampelSN = Ampel('rot')
ampelWO = Ampel('gruen')
ampelmanager = AmpelManager()
ampelmanager.ampel1 = ampelNS
ampelmanager.ampel2 = ampelOW
ampelmanager.ampel3 = ampelSN
ampelmanager.ampel4 = ampelWO
#
#
#
#
#
#
#
#
#
1
2
3
4
5
6
7
8
9
57
Ampeln steuern – Version 1
#-----------------------------------------------------------------# Ampel
#-----------------------------------------------------------------class Ampel(object):
# ... wie bisher
Aufgabe
#-----------------------------------------------------------------# Ampelmanager
#------------------------------------------------------------------
(b) Entwickle ein Testprogramm
zur veränderten
Implementierung.
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.schalten()
self.ampel4.schalten()
(a) Schaue dir den Konstruktor
der Klasse Ampelmanager genau
an. Wozu dienen die Parameter
a1, ..., a4?
Ampeln steuern – Version 2
58
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.
NS
OW
WO
SN
Ampeln steuern – Version 2
59
…
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.
…
# Test
ampelNS = Ampel('rot')
ampelOW = Ampel('gruen')
ampelSN = Ampel('rot')
ampelWO = Ampel('gruen')
ampelnKreuzung = [ampelNS, ampelOW,
ampelSN, ampelWO]
ampelmanager = AmpelManager()
ampelmanager.setListeAmpeln(ampelnKreuzung)
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()
60
Ampeln 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()
ampelmanager.erzeugeAmpeln()
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()
Ampeln steuern – Version 3
61
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()
ampelmanager.erzeugeAmpel('rot')
ampelmanager.erzeugeAmpel('gruen')
ampelmanager.erzeugeAmpel('rot')
ampelmanager.erzeugeAmpel('gruen')
…
62
Beziehung zwischen Objekten
Unter den vielen Beziehungen zwischen Objekten in unserer Lebenswelt spielt die kenntBeziehung 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 SoftwareObjekte mit Attributen versehen, mit deren Hilfe sie Referenzen auf "Beziehungspartner"
verwalten können.
63
Fachkonzept - 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 AmpelmanagerObjekt Zugriff auf die AmpelObjekte 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
64
Beziehungsmuster
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
65
Hat-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:
66
Interaktion 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.
69
Teil 4
Vererbung
70
Gemeinsamkeiten 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.
71
Gemeinsamkeiten 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.
72
Gemeinsamkeiten bei Ampeln
class AmpelAuto(object):
class AmpelFussgaenger(object):
Codeduplizierung
def __init__(self, anfangszustand):
self.zustand = anfangszustand
def __init__(self, anfangszustand):
self.zustand = anfangszustand
def setZustand(self, z):
self.zustand = z
def setZustand(self, z):
self.zustand = z
def getZustand(self):
return self.zustand
def getZustand(self):
return self.zustand
def schalten(self):
if self.zustand == 'rot':
self.zustand = 'rotgelb'
elif self.zustand == 'rotgelb':
…
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, False)
…
return lampen
def getLampen(self):
if self.zustand == 'rot':
lampen = (True, False)
…
return lampen
73
Eine allgemeine Ampelklasse
Auto- und Fußgängerampeln weisen eine Reihe von Gemeinsamkeiten auf. Beim
Weiterschhalten durchlaufen beide zyklisch eine bestimmte Folge von Zuständen.
Eine allgemeine Ampelklasse
74
class Ampel(object):
def __init__(self, pListeZustaende):
self.listeZustaende = pListeZustaende
self.indexAktuellerZustand = 0
def schalten(self):
if self.indexAktuellerZustand < len(self.listeZustaende)-1:
self.indexAktuellerZustand = self.indexAktuellerZustand + 1
else:
self.indexAktuellerZustand = 0
def getZustand(self):
return self.listeZustaende[self.indexAktuellerZustand]
def setZustand(self, z):
self.indexAktuellerZustand = self.listeZustaende.index(z)
Aufgabe:
Simuliere mit dieser Klasse das Verhalten einer Autoampel / Fußgängerampel.
75
Spezielle 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.
Implementierung
76
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.index(anfangszustand)
def getLampen(self):
zustand = self.listeZustaende[self.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
77
Implementierung
Aufgabe 1:
Führe das Programm selbst aus. Kannst du
das Verhalten erklären?
Aufgabe 2:
Entwickle analog eine Klasse
AmpelFussgaenger und teste sie.
# 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()
78
Aufgabe 3:
Vergleiche die
Implementierungen. Worin
unterscheiden sie sich?
Implementierung
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(anfangszustand)
...
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)
...
79
Fachkonzept - 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.
80
Fachkonzept - 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.
81
Teil 5
Miniwelt und Datenmodell
82
Eine 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.
NS
NS
OW
OW
WO
WO
SN
SN
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.
83
Eine 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. …
NS
NS
OW
OW
WO
WO
SN
SN
Eine Miniwelt mit Ampeln
84
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.
NS; SN
schalten
F
T
OW
OW
…
T
T
F
NS; SN
schalten
NS
T
OW; WO
schalten
NS
…
…
F
F
T
WO
WO
…
OW; WO
schalten
T
F
F
SN
SN
Objekte und ihre Zuständigkeiten
85
Aufgabe:
Welche Objekte sollen
bei der Modellierung der
Miniwelt eingeführt
werden?
NS; SN
schalten
F
T
T
F
F
T
T
F
F
NS
F
NS
OW
OW; WO
schalten
T
T
F
F
F
F
T
T
F
F
F
F
T
F
F
T
F
F
F
F
OW
NS; SN
schalten
WO
OW; WO
schalten
WO
T
F
F
F
F
F
F
T
F
SN
F
SN
4
86
Objekte 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.
87
Die Klasse Ampel
Aufgabe:
Beschreibe eine Klasse Ampel / Fußgängerampel mit einem Klassendiagramm.
88
Die 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.
NS; SN
schalten
F
T
T
F
F
T
T
F
F
F
T
T
F
F
F
F
T
T
F
F
F
F
T
F
F
T
F
F
F
F
schalten
T
F
F
F
F
F
F
T
F
F
4
0
1
2
3
4
5
6
7
8
9
OW; WO
schalten
NS; SN
schalten
OW; WO
89
Die 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.
NS; SN
schalten
F
T
T
F
F
T
T
F
F
F
T
T
F
F
F
F
T
T
F
F
F
F
T
F
F
T
F
F
F
F
T
F
F
F
F
F
F
T
F
F
OW; WO
schalten
NS; SN
schalten
OW; WO
schalten
4
90
Die 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.
NS; SN
schalten
F
T
T
F
F
T
T
F
F
NS
F
NS
O
W
OW; WO
schalten
T
T
F
F
F
F
T
T
F
F
F
F
T
F
F
T
F
F
F
F
O
W
NS; SN
schalten
W
O
OW; WO
schalten
T
F
F
F
F
F
F
T
F
F
W
O
SN
SN
4
Die Klasse Ampelmanager
91
4
schalteAmpeln()
1
[listeSchaltsignale[i] == True]
schalten()
ampelmanager
ampelNS
...
...
schalteAmpeln(...)
...
schalten(...)
...
getAktuelleSchaltsignale()
listeSchaltsignale
2
schaltplanverwalter
getStand()
...
getAktuelleSchaltsignale()
...
s
weiterzaehlen()
3
schaltpl....zaehler
...
weiterzaehlen()
getStand()
...
…
92
Implementierung 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.
Miniwelt und Datenmodell
93
Mit Miniwelt bezeichnen wir den Weltausschnitt / Gegenstandsbereich, der dem zu
entwickelnden Programm (Software-System) zu Grunde liegt.
NS
NS; SN
schalten
F
T
T
F
F
T
T
F
F
NS
F
OW
OW
OW; WO
schalten
T
T
F
F
F
F
T
T
F
F
F
F
T
F
F
T
F
F
F
F
NS; SN
schalten
WO
WO
SN
OW; WO
schalten
T
F
F
F
F
F
F
T
F
F
SN
94
Miniwelt 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.
Exkurs - UML
95
UML steht für "uniform modeling language" und ist eine normierte Bildsprache, mit der man
objektorientierte Modelle beschreiben kann.
Objektdiagramm
Exkurs - UML
96
UML steht für "uniform modeling language" und ist eine normierte Bildsprache, mit der man
objektorientierte Modelle beschreiben kann.
Klassendiagramm
Exkurs - UML
97
UML steht für "uniform modeling language" und ist eine normierte Bildsprache, mit der man
objektorientierte Modelle beschreiben kann.
Sequenzdiagramm
Exkurs - UML
98
UML steht für "uniform modeling language" und ist eine normierte Bildsprache, mit der man
objektorientierte Modelle beschreiben kann.
Zustandsdiagramm
99
Teil 6
Datenmodell und GUI
100
Datenmodell und GUI
Wir betrachten hier die Verknüpfung einer grafischen Benutzeroberfläche mit einem
Datenmodell.
101
Verwendung von GUI-Objekten
#--------------------------------------------------# Erzeugung des Datenmodell-Objekts
#--------------------------------------------------from ampel import Ampel
ampel = Ampel('rot')
#--------------------------------------------------Datenmodell-Objekt
# GUI
#--------------------------------------------------from tkinter import *
GUI-Objekt
…
# Ereignisverarbeitung
def buttonWeiterClick():
# Verarbeitung der Daten GUI-Objekt
ampel.schalten()
# Aktualisierung der Anzeige
(lampeRot, lampeGelb, lampeGruen) =
ampel.getLampen()
if lampeRot:
canvas.itemconfigure(id_rot,
fill=rotAn)
…
…
# Erzeugung des Fensters
fenster = Tk()
fenster.title("Ampel")
fenster.geometry("400x300")
# Zeichenfläche
canvas = Canvas(master=fenster)
canvas.place(x=0, y=0, width=400,…)
…
# Rot-Licht
id_rot = canvas.create_oval(252, 122, 260,
130, fill=grau)
…
# Button zum Weiterschalten
buttonWeiter = Button(master=fenster,
text="weiter",
command=buttonWeiterClick)
buttonWeiter.place(x=150, y=270, …)
# Ereignisschleife starten
fenster.mainloop()
102
Objektkonstellation
#--------------------------------------------------# Erzeugung des Datenmodell-Objekts
#--------------------------------------------------…
ampel = Ampel('rot')
#--------------------------------------------------# GUI
#--------------------------------------------------…
# Erzeugung des Fensters
fenster = Tk()
…
# Zeichenfläche
canvas = Canvas(master=fenster)
…
# Button zum Weiterschalten
buttonWeiter = Button(master=fenster,
text="weiter",
command=buttonWeiterClick)
…
Datenmodell-Objekt
GUI-Objekte
103
Objektkonstellation mit GUI-Verwalter
GUI-Objekte-Verwalter
GUI-Objekte
Datenmodell-Objekt
104
Eine GUI-Klasse
…
class GUI(object):
def __init__(self, datenmodell):
# Referenzattribute zum Datenmodell
Kennt Datenmodell
self.ampel = datenmodell[0]
# Erzeugung des Fensters
Hat GUI-Objekt
self.fenster = Tk()
…
# Zeichenfläche
self.canvas = Canvas(master=self.fenster)
self.canvas.place(x=0, y=0, width=400, height=300)
…
# Rot-Licht
self.id_rot = self.canvas.create_oval(252, 122, 260, 130, fill=grau)
…
# Aktualisierung der Anzeige
self.anzeigeAktualisieren()
# Button zum Weiterschalten
self.buttonWeiter = Button(master=self.fenster,
text="weiter",
command=self.buttonWeiterClick)
self.buttonWeiter.place(x=150, y=270, width=100, height=20)
…
105
Eine GUI-Klasse
…
#-----------------------------------------------------------------------------# Erzeugung der Objekte
#-----------------------------------------------------------------------------ampel = Ampel('rot')
datenmodell = [ampel]
gui = GUI(datenmodell)
gui.fenster.mainloop()
106
Datenmodell-GUI-Architektur
Aufgabe:
Das Software-System benutzt eine 2-Schichten-Architektur: Die obere Schicht ist für die GUI
zuständig, die untere für das Datenmodell. Warum ist es günstig, wenn in der unteren Schicht
(d.h. im Datenmodell) keine Bezüge auf die obere Schicht (d.h. die GUI) genommen werden?
107
Trennung Datenmodell-GUI
Die Trennung zwischen Datenmodell und GUI ist ein Prinzip bei der Entwicklung von Systemen
mit grafischer Benutzeroberfläche, das hilft, klar strukturierte und gut wartbare Programme zu
erstellen: Während GUI-Objekte auf Objekte des Datenmodells zugreifen dürfen, ist der
umgekehrte Zugriff nicht erlaubt.
Hinweis:
Im Unterricht reicht in der Regel diese Zwei-Schichten-Architektur.
108
MVC-Architektur
Das Datenmodell erfasst die Miniwelt.
Aufgabe eines GUI-Objekts ist es, die grafische
Benutzeroberfläche zu erzeugen.
Aufgabe eines Controler-Objekts ist es, die Datenmodellund GUI-Objekte zu erzeugen und zu verknüpfen.
109
Das Datenmodell befragen
class GUI(object):
…
def buttonWeiterClick(self):
# Verarbeitung der Daten
self.ampel.schalten()
# Aktualisierung der Anzeige
self.anzeigeAktualisieren()
def anzeigeAktualisieren(self):
(lampeRot, lampeGelb, lampeGruen) = ampel.getLampen()
if lampeRot:
self.canvas.itemconfigure(self.id_rot, fill=rotAn)
else:
self.canvas.itemconfigure(self.id_rot, fill=rotAus)
…
110
Das Datenmodell befragen
Das Objekt gui nutzt hier eine Strategie, die man Befragen oder Polling nennt. Das Objekt gui
"weiß" genau, wann sich das Datenmodell ändert (nach jedem Anklicken einer Schaltfläche)
und "besorgt sich" dann die geänderten Daten zur Anzeige.
Aufgabe:
Erkläre das Verhalten der Objekte anhand des folgenden Sequenzdiagramms.
111
Das Datenmodell beobachten
class Ampel(object):
def __init__(self, anfangszustand):
self.zustand = anfangszustand
self.beobachter = None
def setBeobachter(self, pBeobachter):
self.beobachter = pBeobachter
def setZustand(self, z):
self.zustand = z
self.beobachter.anzeigeAktualisieren()
def schalten(self):
if self.zustand == 'rot':
self.zustand = 'rotgelb'
…
self.beobachter.anzeigeAktualisieren()
…
class GUI(object):
…
def buttonWeiterClick(self):
# Verarbeitung der Daten
self.ampel.schalten()
def anzeigeAktualisieren(self):
…
ampel = Ampel(None)
datenmodell = [ampel]
gui = GUI(datenmodell)
# Beobachter festlegen
ampel.setBeobachter(gui)
ampel.setZustand('rot')
# Ereignisschleife starten
gui.fenster.mainloop()
112
Das Datenmodell beobachten
Das Objekt gui nutzt hier eine Strategie, die man Beobachten nennt. Das Objekt gui wird als
registriertes beobachtendes Objekt des Datenmodell-Objekts jeweils benachrichtigt, wenn sich
das Datenmodell verändert hat.
Aufgabe:
Erkläre das Verhalten der Objekte anhand des folgenden Sequenzdiagramms.
113
Literaturhinweise
Es gibt eine Vielzahl von fachwissenschaftlichen Darstellungen zur
objektorientierten Modellierung und Programmierung. Hier wurden folgende
Lehrwerke benutzt:
- D. J. Barnes, M. Kölling: Objektorientierte Programmierung mit Java. Pearson Studium 2003.
- Helmut Balzert: Lehrbuch Grundlagen der Informatik. Spektrum Ak. Verlag 1999.
- Bernd Oestereich: Objektorientierte Softwareentwicklung. Oldenbourg 1998.
Dagegen gibt es nur wenige Schulbücher, die systematisch in die objektorientierte
Programmierung einführen, z. B.:
- Siegfried Spolwig: Objektorientierung im Informatikunterricht. Dümmler-Verlag
1997.
- P. Damann, J. Wemßen: Objektorientierte Programmierung mit Delphi, Band 2.
Klett-Verlag 2003.
Viele interessante Artikel mit Unterrichtsvorschlägen bzw. fachdidaktischen
Auseinandersetzungen findet man in der Zeitschrift LOG IN. Das Themenheft
128/129 ist speziell dem Thema „Objektorientiertes Modellieren und
Programmieren“ gewidmet.
...
114
Literaturhinweise
Im Internet findet man ebenfalls sehr viele schulgerechte Darstellungen der
objektorientierten Modellierung und Programmierung, z. B:
http://informatikag.bildung-rp.de/
Die AG-Informatik des LMZ in RLP stellt u. a. auch Fortbildungsmaterialien zu
diesem Thema bereit.
http://hsg.region-kaiserslautern.de/faecher/inf/index.php
Auf der Homepage des HSG in Kaiserslautern findet man Unterrichtsmaterialien und
Links zu weiteren interessanten Seiten.
...
Die Darstellung hier orientiert sich an den Materialien auf den Webseiten:
http://www.inf-schule.de