i

Übungen

Aufgabe 1: Eine Implementierung austauschen

Wir betrachten die Klasse Ampel, die durch das folgende Klassendiagramm beschrieben wird.

Klassendiagramm

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.

  1. Ergänze die folgende Implementierung der Klasse Ampel so, dass die Methoden genau dasselbe Verhalten zeigen wie die oben gegebene Implementierung.
  2. 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):
            # ...
    
  3. Warum sollte man keine Zugriffe auf Attribute benutzen? Begründe mit dem Austausch einer Implementierung.

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
  1. 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.
  2. Warum ist es so schwierig, Flüchtigkeitsfehler wie den oben gezeigten zu finden?
  3. Ergänze den fehlerhaften Quelltext zwischen der ersten und der zweiten Zeile durch
        __slots__=('zustand')
    
    Beobachte das veränderte Verhalten bei deinem Test. Welche Bedeutung hat diese Codezeile? Warum sollte man sinnvoller Weise in Python mit "Slots" arbeiten?

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.

Suche

v
7.2.1.2.8
inf-schule.de/oop/python/ampel/modularisierung/uebungen
inf-schule.de/7.2.1.2.8
inf-schule.de/@/page/YyGBoOkqevS0SUpt

Rückmeldung geben