Übungen
Aufgabe 1: Eine Implementierung austauschen
Wir betrachten die Klasse Ampel
, die durch das folgende Klassendiagramm beschrieben wird.
Eine Implementierung dieser Klasse liegt auch bereits vor:
class Ampel(object):
def __init__(self, anfangszustand):
self.zustand = anfangszustand
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'
def getLampen(self):
if self.zustand == 'rot':
lampen = (True, False, False)
elif self.zustand == 'rotgelb':
lampen = (True, True, False)
elif self.zustand == 'gruen':
lampen = (False, False, True)
elif self.zustand == 'gelb':
lampen = (False, True, False)
return lampen
def getZustand(self):
return self.zustand
def setZustand(self, z):
self.zustand = z
Es kommt des öfteren vor, dass eine Implementierung einer Klasse ausgetauscht werden soll. Wir spielen das im Folgenden einmal durch.
-
Ergänze die folgende Implementierung der Klasse
Ampel
so, dass die Methoden genau dasselbe Verhalten zeigen wie die oben gegebene Implementierung. - 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):
# ...
Aufgabe 2: Achtung - Fehlerquelle
Gegeben ist folgende Implementierung der Klasse Ampel
:
class Ampel(object):
def __init__(self, anfangszustand):
self.zustand = anfangszustand
def schalten(self):
if self.zustand == 'rot':
self.zustand = 'rotgelb'
elif self.zustand == 'rotgelb':
self.zustand = 'gruen'
elif self.zustand == 'gruen':
self.zustnd = 'gelb'
elif self.zustand == 'gelb':
self.zustand = 'rot'
def getLampen(self):
if self.zustand == 'rot':
lampen = (True, False, False)
elif self.zustand == 'rotgelb':
lampen = (True, True, False)
elif self.zustand == 'gruen':
lampen = (False, False, True)
elif self.zustand == 'gelb':
lampen = (False, True, False)
return lampen
def getZustand(self):
return self.zustand
def setZustand(self, z):
self.zustand = z
-
Teste diese Implementierung der Klasse
Ampel
. Irgend etwas stimmt hier nicht. Findest du den Fehler? Erkläre, was hier schiefläuft. Es gibt zur Fehlersuche zwei hilfreiche Möglichkeiten: Für beide musst du das Ampel-Objekt, das du beim Testen erstellt hast, genauer untersuchen.- Nutze in Thonny den Objektinspektor und sieh dir die Attribute des Ampel-Objekts an.
- Lass dir den Wert des Attributs
__dict__
anzeigen. - Erzeuge ein Ampel-Objekt und sende ihm mehrfach die Nachricht "schalten()". Beobachte.
- Warum ist es so schwierig, Flüchtigkeitsfehler wie den oben gezeigten zu finden?
-
Ergänze den fehlerhaften Quelltext zwischen der ersten und der zweiten Zeile durch
__slots__=('zustand')
Aufgabe 3: Ein Dialog über Modularisierung
Zwei Softwareentwickler(innen) - Isa und Otti - unterhalten sich über die Bausteine, die sie
benutzen. Wo liegen die Gemeinsamkeiten, wo die Unterschiede?
Isa | Otti |
---|---|
Hey Otti, heute schon Objekte erzeugt? | |
Hallo Isa, was machen die Funktionen? | |
Also, jetzt mal im Ernst, ich finde das ziemlich kompliziert, was du da treibst. | |
Wirklich? Ich versuche ja nur, das Modularisierungsprinzip zu beachten. | |
Das versuche ich doch auch - nur mit Funktionen statt Objekten. | |
Stimmt! | |
Funktionen kommen mir aber viel einfacher vor als Objekte mit all ihren Attributen und Methoden. Warum also solch komplizierte Bausteine, wenn es auch viel einfacher geht? | |
Es kommt halt darauf an, was man mit den Bausteinen anfangen will. | |
Wie soll ich das verstehen? | |
Also, wie ist das im normalen Leben? Wenn man sich einen Drachen bauen will, dann reichen eigentlich Holzstäbe, etwas Papier, eine lange Schnur usw.. Wenn man sich ein Fahrrad selbst zusammenbauen will, dann ist es günstig, wenn man sich einen Bausatz mit vorgefertigten Bauteilen besorgt, die schon die Grundfunktionen mitbringen. Und so ist es halt auch bei der Softwareentwicklung. | |
Du meinst, Funktionen benutzt man, wenn man kleine Probleme löst, und Objekte, wenn komplexere Aufgaben anstehen. | |
Genau so sehe ich das. Wenn man komplexere Aufgaben erledigen will, dann fallen meist sehr viele zu verwaltende Daten an und es müssen eine Vielzahl an Funktionen zur Verarbeitung der Daten konzipiert werden. Um etwas Ordnung in dieses Daten- und Funktionschaos zu bringen, bündelt man Zusammengehörendes zu einer neuen Einheit zusammen. Oft ist es doch so, dass auf bestimmte Daten nur ganz bestimmte Operationen angewandt werden sollen. Da ist es dann günstig, wenn man aus diesen Komponenten - also Daten und zugehörige Operationen - eine komplexen Baustein erzeugt. | |
Ok! | |
Also, im Grunde machen wir dasselbe, du im Kleinen, ich im Großen. Dass bei mir die Bausteine etwas komplizierter wirken, liegt also in der Natur der Sache. |