Die Präsentation wird geladen. Bitte warten

Die Präsentation wird geladen. Bitte warten

Konzepte objektorientierter Programmierung Klaus Becker 2009.

Ähnliche Präsentationen


Präsentation zum Thema: "Konzepte objektorientierter Programmierung Klaus Becker 2009."—  Präsentation transkript:

1 Konzepte objektorientierter Programmierung Klaus Becker 2009

2 2 Objektorientierung "Objektorientierung ist die derzeitige Antwort auf die gestiegene Komplexität der Softwareentwicklung." Oestereich: Objektorientierte Software-Entwicklung

3 3 Teil 1 Objekte

4 4 Verwaltung von Bankkonten Ziel ist es, ein System zur Verwaltung von Bankkonten zu entwickeln. Dieses System soll recht einfach gestaltet werden, um es leicht durchschaubar zu halten. Aus diesem Grund werden wir auf viele Aspekte eines realen Bankkontenverwaltungssystem verzichten. S Konto-Nr Freunde des BurggymnasiumsKontoauszug Blatt 342 DatumErläuterungBetrag Miete der Fruchthalle - Sommerkonzert Spende eines ehemaligen Schülers Zuschuss zum Whiteboard Kontostand in EUR,

5 5 Verwaltung von Bankkonten Wesentliche zu verwaltende Daten erkennt man auf einem Kontoauszug. S Konto-Nr Freunde des BurggymnasiumsKontoauszug Blatt 342 DatumErläuterungBetrag Miete der Fruchthalle - Sommerkonzert Spende eines ehemaligen Schülers Zuschuss zum Whiteboard Kontostand in EUR, Kontostand KontonummerKontoinhaber auszahlen einzahlen auszahlen Ein Bankkonto hat eine bestimmte Kontonummer (und einen Kontoinhaber - von dem wir vorerst einmal absehen). Ein Bankkonto hat zudem einen bestimmten Stand - das ist der Geldbetrag, der aktuell auf dem Konto verfügbar ist. Von einem Bankkonto kann man Geldbeträge auszahlen, man kann aber Geldbeträge auf ein Konto einzahlen.

6 6 Objektorientierter Modellierungsansatz So wie die zu verwaltende Welt aus Objekten - hier Konten - besteht, so soll das Verwaltungssystem aus Softwareobjekten aufgebaut werden. S Konto-Nr Freunde des BurggymnasiumsKontoauszug Blatt 342 DatumErläuterungBetrag Miete der Fruchthalle - Sommerkonzert Spende eines ehemaligen Schülers Zuschuss zum Whiteboard Kontostand in EUR, Softwareobjekt

7 7 Softwareobjekt in Aktion >>> konto = Konto(234126) >>> konto.nr >>> konto.stand 0 >>> konto.stand = >>> konto.stand >>> konto.auszahlen(450.0) >>> konto.stand >>> konto.einzahlen(1000.0) >>> konto.stand >>> konto.auszahlen(800.0) >>> konto.stand Wenn man das Programm konto.py ausführt, dann lässt sich folgender Python-Dialog führen. Probiere das einmal aus.

8 8 Objekte 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

9 9 Objekte Objekt Attribute - Attributwerte Ausführung einer Methode Zugriff auf Attribute: konto.stand = objekt.attribut Aktivierung von Methoden: konto.einzahlen(1000.0) objekt.methode

10 10 Übungen Aufgabe 1 (siehe ) Lade die Datei wuerfel.txt herunter (benenne sie in wuerfel.py um) und führe einen Python- Dialog analog zum folgenden: >>> w = Wuerfel() >>> w.augen 1 >>> w.werfen() >>> w.augen 6 (a) Was leistet das Attribut augen des Objekts w, was die Methode werfen()? (b) Beschreibe den gezeigten Ablauf mit Hilfe von Objektdiagrammen.

11 11 Teil 2 Klassen

12 12 Ein Bauplan für Konto-Objekte class Konto(object): def __init__(self, nummer): self.nr = nummer self.stand = 0 def einzahlen(self, betrag): self.stand = self.stand + betrag def auszahlen(self, betrag): #... Aufgabe 1: Analysiere diese Klassendeklaration und ergänze den fehlenden Teil #.... Überprüfen kannst du deinen Lösungsvorschlag, indem du die Klassendeklaration unter einem geeigneten Namen (z. B. konto.py) abspeicherst und ausführst. Wenn du alles richtig gemacht hast, dann sollte z. B. der oben gezeigte Python-Dialog möglich sein. >>> k1 = Konto(5) >>> k1.nr 5 >>> k1.stand 0 >>> k1.einzahlen(100.0) >>> k1.stand 100.0

13 13 Ein Bauplan für Konto-Objekte class Konto(object): def __init__(self, nummer): self.nr = nummer self.stand = 0 def einzahlen(self, betrag): self.stand = self.stand + betrag def auszahlen(self, betrag): #... Aufgabe 2: Versuche auch einmal, mehrere Konto-Objekte zu erzeugen. Überweise mit passenden Methoden (Euro) vom Konto mit der Kontonummer 5 auf das Konto mit der Kontonummer 8. >>> k1 = Konto(5) >>> k1.einzahlen(1000.0) >>> k2 = Konto(8) >>>...

14 14 Klassen 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. Objektdiagramm 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. Klassendiagramm

15 15 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. Ein Software-Objekt hat - wie viele 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. Konstruktor

16 16 Klassendeklaration in Python Klassenname class Konto(object): def __init__(self, nummer): self.nr = nummer self.stand = 0 def einzahlen(self, betrag): self.stand = self.stand + betrag def auszahlen(self, betrag): self.stand = self.stand - betrag Doppelpunkt Einrückung Schlüsselwort Oberklasse Attribute Konstruktor Methode Referenz auf Objekt

17 17 Objekterzeugung in Python class Konto(object): def __init__(self, nummer): self.nr = nummer self.stand = 0 def einzahlen(self, betrag): self.stand = self.stand + betrag def auszahlen(self, betrag): self.stand = self.stand - betrag >>> k1 = Konto(5) >>> k2 = Konto(8) >>> k1.nr 5 >>> k1.stand 0 >>> k2.nr 8 >>> k2.stand 0 >>> k1.__dict__ {'nr': 5, 'stand': 0} >>> k2.__dict__ {'nr': 8, 'stand': 0} >>> k = Konto(5) >>> k.stand 0.0 >>> del k >>> k.stand Traceback (most recent call last): File... NameError: name 'k' is not defined >>> Klassendeklaration Erzeugung eines Objekts Vernichtung eines Objekts Inspektion eines Objekts

18 18 Übungen Aufgabe 1 (siehe ) Gegeben ist eine Implementierung der Klasse Wuerfel(): from random import randint class Wuerfel(object): def __init__(self): self.augen = 1 def werfen(self): self.augen = randint(1, 6) Erzeuge drei Objekte der Klasse Wuerfel und würfele hiermit solange, bis mindestens einer der Würfel eine 6 liefert.

19 19 Übungen Aufgabe 2 (siehe ) Gegeben ist das folgende Klassendiagramm zur Klasse Bruch: Was soll mit einem Objekt der Klasse Bruch beschrieben werden? Entwickle eine geeignete Implementierung und teste sie mit einem Python-Dialog.

20 20 Übungen Aufgabe 3 (siehe ) Wenn man modulo einer vorgegebenen Zahl (man nennt sie auch Modul) zählt, dann bildet man jeweils den Rest bei der Division durch die vorgegebene Zahl. Betrachte als Beispiel die vorgegebene Zahl (Modul) 5. Wenn man modulo 5 zählt, dann geht das so: 0 modulo 5, 1 modulo 5, 2 modulo 5, 3 modulo 5, 4 modulo 5, 5 modulo 5, 6 modulo 5,... Berechnet man jeweils die Reste, dann ergibt das folgende die Zahlenfolge 0, 1, 2, 3, 4, 0, 1,.... Wenn man modulo einer vorgegebenen Zahl n zählt, dann ergibt das also die Zahlenfolge 0, 1,..., (n-1), 0, 1,.... Konzipiere eine Klasse ModuloZaehler (mit einem Klassendiagramm), die Python-Dialoge wie den folgenden ermöglicht. Implementiere die Klasse und teste sie mit geeigneten Python-Dialogen. >>> z = ModuloZaehler(3) >>> z.modul 3 >>> z.stand 0 >>> z.weiterzaehlen() >>> z.stand 1 >>> z.weiterzaehlen() >>> z.stand 2 >>> z.weiterzaehlen() >>> z.stand 0 >>> z.zurueckzaehlen() >>> z.stand 2 >>> z.nullsetzen() >>> z.stand 0

21 21 Übungen Aufgabe 5 (siehe ) Die Klasse Schlange kann man verwenden, um Warteschlangen zu simulieren. Erläutere, was die Methoden der Klasse Schlange bewirken. Verdeutliche deine Erläuterungen jeweils mit einem geeigneten Python-Protokoll. class Schlange(object): def __init__(self): self.liste = [] def istLeer(self): if self.liste == []: return True else: return False def mitLetztem(self, element): self.liste = self.liste + [element] def ohneErstes(self): if not self.istLeer(): self.liste = self.liste[1:]... def anzahlElemente(self): return len(self.liste) def getSchlange(self): return self.liste def setSchlange(self, liste): self.liste = liste

22 22 Übungen Aufgabe 6 (siehe ) Die folgende Deklaration des Konstruktors erlaubt es, Objekte flexibel mit Anfangswerten zu versehen: class Konto(object): def __init__(self, nummer=0, betrag=0): self.nr = nummer self.stand = betrag def einzahlen(self, betrag): self.stand = self.stand + betrag def auszahlen(self, betrag): self.stand = self.stand - betrag >>> k1 = Konto() >>> k1.nr 0 >>> k1.stand 0 >>> k2 = Konto(1) >>> k2.nr 1 >>> k2.stand 0 >>> k3 = Konto(2, ) >>> k3.nr 2 >>> k3.stand >>> k4 = Konto(betrag=400.0) >>> k4.nr 0 >>> k4.stand Erkläre, wie die jeweiligen Attributwerte der Objekte zustande kommen.

23 23 Teil 3 Verwaltung von Objekten

24 24 Objekte und ihre Identität Python-Dialog Aufgabe: Was verbirgt sich wohl hinter der Identitätsnummer eines Objekts? Prüfe mit geeigneten Python-Dialogen, ob sich die Identitätsnummer eines Objekts ändert, wenn sich der Zustand eines Objekts beim Ausführen einer Methode ändert. >>> k1 = Konto(3) >>> k1 >>> id(k1) >>> hex( ) '0x135d670' >>> k2 = Konto(4) >>> id(k2)

25 25 Konto kopieren? Python-Dialog Aufgabe: Stell Vermutungen auf, was anstelle der drei Fragezeichen jeweils steht. Teste, ob deine Vermutungen stimmen. Kannst du dir die Ergebnisse erklären? Benutze auch den id-Operator, um Einsicht über die verwalteten Objekte zu erhalten. >>> k1 = Konto(6) >>> k2 = k1 >>> k1.stand ??? >>> k2.stand ??? >>> k2.einzahlen(100.0) >>> k1.stand ??? >>> k2.stand ??? >>> k2 = Konto(7) >>> k1.stand ??? >>> k2.stand ??? >>>

26 26 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.

27 27 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.

28 28 Zuweisungen bei Zeigervariablen k1 = Konto(6) k2 = k1 k2.einzahlen(100.0) k2 = Konto(7)

29 29 Objekte in Python Veränderbares Objekt Unveränder- bares Objekt Seiteneffekt kein Seiteneffekt from konto import * def null(konto): print(id(konto)) konto.stand = 0.0 print(id(konto)) k = Konto(9) k.einzahlen(100.0) print(k.stand) print(id(k)) null(k) print(k.stand) print(id(k)) def null(zahl): print(id(zahl)) zahl = 0.0 print(id(zahl)) z = print(z) print(id(z)) null(z) print(z) print(id(z)) Python unterscheidet zwischen veränder- baren und unveränderbaren Objekten.

30 30 Übungen Aufgabe 1 (siehe ) Wenn man den Objektbezeichner vom Python-Interpreter auswerten lässt, dann wird der vom Bezeichner verwaltete Wert angezeigt. Warum zeigt der Python-Dialog, dass hier Zeiger verwaltet werden? >>> k1 = Konto(6) >>> k2 = k1 >>> k1 >>> k2 >>> k2 = Konto(8) >>> k2 class Konto(object): def __init__(self, nummer): self.nr = nummer self.stand = 0 def einzahlen(self, betrag): self.stand = self.stand + betrag def auszahlen(self, betrag): self.stand = self.stand - betrag

31 31 Übungen Aufgabe 2 (siehe ) Jedes Objekt wird zur Identifierung mit einer eindeutigen Identitätsnummer versehen. Mit Hilfe des id-Operators lässt sich diese Identitätsnummer in Python ermitteln. Was zeigt der abgebildete Python-Dialog? >>> k1 = Konto(6) >>> k2 = k1 >>> id(k1) >>> id(k2) >>> k2 = Konto(8) >>> id(k2) class Konto(object): def __init__(self, nummer): self.nr = nummer self.stand = 0 def einzahlen(self, betrag): self.stand = self.stand + betrag def auszahlen(self, betrag): self.stand = self.stand - betrag

32 32 Übungen Aufgabe 3 (siehe ) Die Funktion mitNeuemErsten soll dazu dienen, in einer Liste das erste Element auszutauschen. Vegleiche die beiden folgenden Implementierungen und erkläre das unterschiedliche Verhalten. def mitNeuemErsten(liste, element): hilf = [element] + liste[1:] return hilf L = [1, 2, 3] M = mitNeuemErsten(L, 0) print(L) print(M) def mitNeuemErsten(liste, element): liste[0] = element return liste L = [1, 2, 3] M = mitNeuemErsten(L, 0) print(L) print(M)

33 33 Teil 4 Modularisierung und Datenkapselung

34 34 Konto überziehen >>> k = Konto(9) >>> k.stand = >>> k.stand >>> auszahlungsbetrag = >>> k.stand = k.stand - auszahlungsbetrag >>> k.stand Implementierung der Klasse Nutzung der Klasse Ein Konto soll höchstens um 1000 Euro überzogen werden dürfen. class Konto(object): def __init__(self, nummer): self.nr = nummer self.stand = 0 self.minimum = def einzahlen(self, betrag): self.stand = self.stand + betrag def auszahlen(self, betrag): if self.stand - betrag >= self.minimum: self.stand = self.stand - betrag else: print("Auszahlung nicht möglich!") Aufgabe 1: Warum ist dieser Dialog nicht im Sinne des Bankkontenverwaltungssystems?

35 35 Konto überziehen from konto import Konto k = Konto(9) # Testlauf k.einzahlen(600.0) print(k.getStand()) auszahlungsbetrag = k.auszahlen(auszahlungsbetrag) print(k.getStand()) Schnittstelle zur Klasse Nutzung der Klasse Die Entwickler der Klasse Konto veröffentlichen folgende Schnittstelle dieser Klasse: Aufgabe 2: Warum macht es hier Sinn, die Attribute der Klasse Konto nicht zu veröffentlichen und eine Veränderung von Attributwerten nur über veröffentlichte Merhoden zu erlauben? Aufgabe 3: Im Testprogramm wird die Methode getStand benutzt, die in der Schnittstelle vorgesehen ist. Was soll diese Methode leisten? Ergänze die oben gezeigte Implementierung der Klasse Konto um die noch fehlenden Methoden und führe das Testprogramm aus.

36 36 Modularisierung "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." 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. Ein Softwaremodul ist eine in sich abgeschlossene Einheit, die man vielfältig bei Problem- lösungen einsetzen kann. Es reicht dabei zu wissen, welche Operationen die Einheit dem Benutzer zur Verfügung stellt. Wie die Operationen programmiert sind, muss man dagegen nicht wissen. Grundidee der objektorientierten Modularisierung ist es, Softwaremodule als Klassen zu konzipieren.

37 37 Modularisierung in Python from konto import Konto k = Konto(9) # Testlauf k.einzahlen(600.0) print(k.getStand()) auszahlungsbetrag = k.auszahlen(auszahlungsbetrag) print(k.getStand()) from konto import * k = Konto(9) # Testlauf k.einzahlen(600.0) print(k.getStand()) auszahlungsbetrag = k.auszahlen(auszahlungsbetrag) print(k.getStand()) import konto k = konto.Konto(9) # Testlauf k.einzahlen(600.0) print(k.getStand()) auszahlungsbetrag = k.auszahlen(auszahlungsbetrag) print(k.getStand()) Der Name "Konto" wird in den aktuellen Namensraum übernommen.

38 38 Geheimnisprinzip / Datenkapselung 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. Bei der objektorientierten Software-Entwicklung geht man völlig analog vor. Nach dem Geheimnisprinzip werden die internen Daten eines Objekts (die in der Regel über Attribute verwaltet werden) so verborgen, dass ein direkter Zugriff hierauf nicht möglich ist. Jeder Zugriff auf interne Daten und jede Veränderung von internen Daten darf nur über spezielle, hierfür vorgesehene Methoden erfolgen. Man nennt diese Vorgehensweise auch Datenkapselung.

39 39 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.

40 40 Zugriffsrechte in Python Ein Attribut / eine Methode wird in Python zu einem privaten Attribut, wenn der Attributname mit zwei Unterstrichen beginnt und nicht mit Unterstrichen endet. Beginnt der Attributname / Methodenname nicht mit einem Unterstrich, so ist das Attribut öffentlich. class Konto(object): def __init__(self, nummer): self.__nr = nummer self.__stand = 0 self.__minimum = def einzahlen(self, betrag): self.__stand = self.__stand + betrag def auszahlen(self, betrag): if self.__stand - betrag >= self.__minimum: self.__stand = self.__stand - betrag else: print("Auszahlung nicht möglich!") def getStand(self): return self.__stand def setStand(self, stand): if stand >= self.__minimum: self.__stand = stand else: print("Initialisierung nicht möglich!") #...

41 41 Datenkapselung in Python Wie erwartet kann man auf das private Attribut __stand des neu erzeugten Objekts k nicht zugreifen. Python meldet als Fehler, dass es kein Attribut __stand gibt. Der Aufruf k.__dict__ verrät, woran das liegt. Ein Aufruf wie k.__dict__ listet sämtliche Attribute mit den zugehörigen Attributwerten des betreffenden Objekts auf. Interessant ist hier, dass sich das private Attribut __stand hinter einem anderen Namen versteckt. Wenn man weiß, wie der neue Name - hier _Konto__stand - gebildet wird, dann kann man durchaus 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. >>> k = Konto(3) >>> k.__stand Traceback (most recent call last): File... AttributeError: 'Konto' object has no attribute '__stand' >>> k.__dict__ {'_Konto__minimum': , '_Konto__nr': 3, '_Konto__stand': 0} >>> k._Konto__stand 0

42 42 Datenkapselung in Python Ein erster Zugriff auf das private Attribut __stand scheitert. Dann aber ist es - entgegen aller Zugriffslogik - scheinbar möglich, dem privaten Attribut __stand einen Wert zuzuweisen. Der Aufruf k.__dict__ erklärt erst, was hier passiert ist. Neben dem privaten Attribut __stand, das sich hinter dem neuen Namen _Konto__stand versteckt, gibt es noch öffentliches Attribut __stand, auf das man direkt zugreifen kann. >>> k = Konto(3) >>> k.__stand Traceback (most recent call last): File... AttributeError: 'Konto' object has no attribute '__stand' >>> k.__dict__ {_Konto__minimum': , '_Konto__nr': 3, '_Konto__stand': 0} >>> k.__stand = >>> k.__stand >>> k.__dict__ {_Konto__minimum': , '_Konto__nr': 3, '_Konto__stand': 0, '__stand': 100.0} Wir werden im Folgenden bei der Implementierung von Klassen in Python keine Attribute als private Attribute deklarieren. In der Regel werden wir auch keine Zugriffsmethoden einführen und nutzen. Nur in begründeten Ausnahmefällen werden wir von dieser Vereinbarung abweichen.

43 43 Schnittstellen 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. Konto(nummer: int) nachher: Ein Objekt der Klasse Konto ist erzeugt. Der Wert des Attributs nr entspricht dem übergebenen Wert des Parameters nummer, der Wert des Attributs stand beträgt 0, der Wert des Attributs minimum beträgt auszahlen(betrag: float) vorher: Das Konto-Objekt hat einen beliebigen Zustand. nachher: Der Wert des Attributs stand des Konto-Objekts ist um den übergebenen Wert des Parameters betrag reduziert.

44 44 Übungen Aufgabe 1 (siehe ) Zur Klasse Wuerfel soll folgende Implementierung in einer Datei wuerfel.py abgespeichert sein: from random import randint class Wuerfel(object): def __init__(self): self.augen = 1 def werfen(self): self.augen = randint(1, 6) from wuerfel import * # Würfelprogramm w = Wuerfel() w.werfen() versuche = 1 while w.augen != 6: w.werfen() versuche = versuche + 1 print("Versuche:", versuche) (a) Was leistet das folgende Programm, das das Modul Wuerfel als Baustein benutzt? (b) Es soll getestet werden, wie oft man drei Würfel werfen muss, bis mindestens einer eine 6 liefert. Entwickle ein geeignetes Simulationsprogramm, das die Klasse Wuerfel als Baustein benutzt.

45 45 Übungen Aufgabe 2 (siehe ) Folgende Klassen sollen als Bausteine zur Simulation des Spiels "chuck a luck" zur Verfügung gestellt werden: >>> k = Konto(100) >>> s = Spielzahl() >>> wA = Wuerfel() >>> wB = Wuerfel() >>> wC = Wuerfel() >>> k.abheben(1) >>> k.stand 99 >>> s.setzen(3) >>> s.zahl 3 >>> wA.werfen() >>> wB.werfen() >>> wC.werfen() >>> wA.augen 3 >>> wB.augen 6 >>> wC.augen 2 >>> k.einzahlen(2) >>> k.stand 101 (a) Implementiere diese Klassen. Mit Hilfe dieser Bausteine sollen dann Python-Dialoge wie der folgende möglich sein. (b) Mit Hilfe eines Simulationsprogramms soll ermittelt werden, ob das chuck-a-luck-Spiel fair ist.

46 46 Übungen Aufgabe 3 (siehe ) (a) Erstell ein Klassendiagramm und eine Schnittstellenbeschreibung zur Klasse Bruch. (b) Entwickle ein Testprogramm, das die Klasse Bruch als Modul benutzt.... def vollstaendigKuerzen(self): # ggT von Zähler und Nenner best. x = self.zaehler y = self.nenner while y > 0: h = x % y x = y y = h ggt = x # kürzen self.kuerzen(ggt) def add(self, b): x1 = self.zaehler x2 = self.nenner y1 = b.zaehler y2 = b.nenner z1 = x1*y2 + x2*y1 z2 = x2*y2 self.zaehler = z1 self.nenner = z2 self.vollstaendigKuerzen() class Bruch(object): def __init__(self, z, n): self.zaehler = z self.nenner = n def erweitern(self, k): self.zaehler = self.zaehler * k self.nenner = self.nenner * k def kuerzen(self, k): if (self.zaehler % k == 0) and \ (self.nenner % k == 0): self.zaehler = self.zaehler // k self.nenner = self.nenner // k... (c) Füge der Klasse Bruch weitere Operationen hinzu und teste diese Erweiterung.

47 47 Übungen Aufgabe 5 (siehe ) Teste die Einbindung folgender Modulimporte und ihre Auswirkung auf den globalen Namensraum. Warum wird bei sehr umfangreichen Modulen empfohlen, die erste oder dritte der oben aufgelisteten Einbindungsvarianten zu benutzen? >>> from random import randint >>> globals() >>> from random import * >>> globals() >>> import random >>> globals()

48 48 Übungen Aufgabe 6 (siehe ) (a) Teste diese Implementierung der Klasse Bruch. Irgend etwas stimmt hier nicht. Findest du den Fehler? Benutze die Operation __dict__() zur Fehlersuche. Erkläre, was hier schiefläuft. (b) Warum ist es so schwierig, Flüchtigkeitsfehler wie den oben gezeigten zu finden? class Bruch(object): def __init__(self, z, n): self.zaehler = z self.nenner = n def erweitern(self, k): self.zahler = self.zaehler * k self.nenner = self.nenner * k def kuerzen(self, k): if (self.zaehler % k == 0) and \ (self.nenner % k == 0): self.zaehler = self.zaehler // k self.nenner = self.nenner // k

49 49 Teil 5 Beziehungen

50 50 Verwaltung des Kontoinhabers S Konto-Nr.5Adriana MüllerKontoauszug Blatt 3 DatumErläuterungBetrag Handykosten Zuschuss von Oma und Opa Schuhe Kontostand in EUR, Der Kontoinhaber soll jetzt ebenfalls mitverwaltet werden. Hierzu muss das Objektmodell erweitert werden. Mehrere Vorschläge stehen zur Diskussion Aufgabe: Vergleiche die folgenden Vorschläge. Vorschlag 1: Vorschlag 2:

51 51 Verwaltung des Kontoinhabers Aufgabe: Welche Nachteile zeigen sich bei Vorschlag 2, wenn es mehrere Konten und mehrere Kunden gibt? Vergleiche Vorschlag 2 mit Vorschlag 3. Vorschlag 3: Vorschlag 2:

52 52 Verwaltung des Kontoinhabers Aufgabe: Erkläre, wie die Objektkonstellation aus Vorschlag 3 hier realisiert wird. Stell auch eine Vermutung auf, was das Testprogramm auf dem Bildschirm ausgibt. class Konto(object): def __init__(self, nummer): self.nr = nummer self.stand = 0.0 self.inhaber = None def einzahlen(self, betrag): self.stand = self.stand + betrag def auszahlen(self, betrag): self.stand = self.stand - betrag class Kunde(object): def __init__(self, name, vorname): self.name = name self.vorname = vorname from konto_kunde import * # Erzeugung der Objekte konto1 = Konto(5) konto2 = Konto(11) konto2.stand = kunde1 = Kunde("Müller", "Adriana") kunde2 = Kunde("Meier", "Anatol") konto1.inhaber = kunde2 konto2.inhaber = kunde1 # Ausgaben print("Kontonummer: ", konto1.nr) print("Inhaber(in): ", konto1.inhaber.vorname, \ konto1.inhaber.name) print("Kontostand: ", konto1.stand) print() print("Kontonummer: ", konto2.nr) print("Inhaber(in): ", konto2.inhaber.vorname, \ konto2.inhaber.name) print("Kontostand: ", konto2.stand)

53 53 Beziehung Wenn ein Objekt über einen Zeiger (eine Referenz) Zugriff auf ein anderes Objekt hat, so liegt eine (gerichtete) Beziehung zwischen den Objekten vor. Objektdiagramm Klassendiagramm Mit Hilfe eines Aufrufs wie z.B. konto1.inhaber kann das Konto-Objekt konto1 auf Daten des zugeordneten Kunde- Objekts zugreifen, z.B. mit konto1.inhaber.name auf das entsprechende Attribut. Das Konto- Objekt konto1 hat somit Zugriff auf ein Kunde- Objekt, z.B. kunde2. Man sagt auch, dass das Konto-Objekt konto1 in Beziehung zum Kunde- Objekt kunde2 steht.

54 54 Beziehungsmuster Muster: Objekt der Klasse A kennt 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 Muster: Objekt der Klasse A erzeugt Objekt der Klasse B

55 55 Implementierung von Beziehungen Muster: Objekt der Klasse A kennt Objekt der Klasse B class Konto(object): def __init__(self, nummer): self.nr = nummer self.stand = 0.0 self.inhaber = None def einzahlen(self, betrag): self.stand = self.stand + betrag def auszahlen(self, betrag): self.stand = self.stand - betrag class Kunde(object): def __init__(self, name, vorname): self.name = name self.vorname = vorname from bank0 import * # Erzeugung der Objekte konto1 = Konto(5) konto2 = Konto(11) konto2.stand = kunde1 = Kunde("Müller", "Adriana") kunde2 = Kunde("Meier", "Anatol") konto1.inhaber = kunde2 konto2.inhaber = kunde1

56 56 Implementierung von Beziehungen Muster: Objekt der Klasse A kennt Objekt der Klasse B und umgekehrt class Konto(object): def __init__(self, nummer): self.nr = nummer self.stand = 0.0 self.inhaber = None def einzahlen(self, betrag): self.stand = self.stand + betrag def auszahlen(self, betrag): self.stand = self.stand - betrag class Kunde(object): def __init__(self, name, vorname): self.name = name self.vorname = vorname self.konto = None from bank1 import * # Erzeugung der Objekte konto1 = Konto(5) konto2 = Konto(11) konto2.stand = kunde1 = Kunde("Müller", "Adriana") kunde2 = Kunde("Meier", "Anatol") konto1.inhaber = kunde2 konto2.inhaber = kunde1 kunde1.konto = konto2 kunde2.konto = konto1

57 57 Implementierung von Beziehungen Muster: Objekt der Klasse A kennt mehrere Objekte der Klasse B class Konto(object): def __init__(self, nummer): self.nr = nummer self.stand = 0.0 self.inhaber = None def einzahlen(self, betrag): self.stand = self.stand + betrag def auszahlen(self, betrag): self.stand = self.stand - betrag class Kunde(object): def __init__(self, name, vorname): self.name = name self.vorname = vorname self.konten = [] def kontoHinzufuegen(self, konto): self.konten = self.konten + [konto] from bank2 import * # Erzeugung der Objekte konto1 = Konto(5) konto2 = Konto(11) konto2.stand = konto3 = Konto(19) konto3.stand = konto4 = Konto(21) konto4.stand = kunde1 = Kunde("Müller", "Adriana") kunde2 = Kunde("Meier", "Anatol") konto1.inhaber = kunde2 konto2.inhaber = kunde1 konto3.inhaber = kunde1 konto4.inhaber = kunde1 kunde1.kontoHinzufuegen(konto2) kunde1.kontoHinzufuegen(konto3) kunde1.kontoHinzufuegen(konto4) kunde2.kontoHinzufuegen(konto1) Liste

58 58 Implementierung von Beziehungen Muster: Objekt der Klasse A erzeugt Objekte der Klasse B class Konto(object): def __init__(self, nummer): self.nr = nummer self.stand = 0.0 self.inhaber = None def einzahlen(self, betrag): self.stand = self.stand + betrag def auszahlen(self, betrag): self.stand = self.stand - betrag class Bank(object): def __init__(self): self.konten = [] self.naechsteKontoNr = 0 def erzeugeKonto(self): konto = Konto(self.maxKontoNr) self.konten = self.konten + [konto] self.naechsteKontoNr = self.naechsteKontoNr + 1 from bank3 import * from random import * # Erzeugung der Objekte bank = Bank() for i in range(8): bank.erzeugeKonto() for konto in bank.konten: konto.einzahlen(float(randint(0, 100)))

59 59 Operationen als Dienste 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. Ein Bank-Objekt bietet z.B. den Dienst ueberweisen an. Ein Konto-Objekt bietet z.B. den Dienst einzahlen an.

60 60 Interaktion zwischen Objekten Wenn das Bank-Objekt den Dienst "ueberweisen" ausführt, dann schickt es zunächst eine Nachricht auszahlen(...) an das betreffende Konto-Objekt und anschließend eine Nachricht einzahlen(...) an das andere betreffende Konto-Objekt. Das Bank-Objekt interagiert also hier mit zwei Konto-Objekten. Dies ist deshalb möglich, weil das Bank-Objekt gemäß Klassendiagramm oben in Beziehung zu den Konto-Objekten steht. Die Analogien zur Lebenswelt ermöglichen es, die Ausführung objektorientierter Programme in einem Rollenspiel zu verdeutlichen, bei dem Personen die Rolle von Objekten übernehmen.

61 61 Interaktion zwischen Objekten 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. Das Versenden von Nachrichten wird als Interaktion zwischen Objekten gedeutet. Voraussetzung für eine Interaktion zwischen Objekten ist, dass diese miteinander in Beziehung stehen. Wenn das Bank-Objekt den Dienst "ueberweisen" ausführt, dann schickt es zunächst eine Nachricht auszahlen(...) an das betreffende Konto-Objekt und anschließend eine Nachricht einzahlen(...) an das andere betreffende Konto-Objekt. Das Bank-Objekt interagiert also hier mit zwei Konto-Objekten. Dies ist deshalb möglich, weil das Bank-Objekt gemäß Klassendiagramm oben in Beziehung zu den Konto-Objekten steht. Sequenzdiagramm

62 62 Übungen Aufgabe 1 (siehe ) Erzeuge Objekte, die folgende Situation modellieren: Kunde Werner Schmidt hat das Konto mit der Nummer 23. Kundin Theresa Meier hat das Konto mit der Nummer 42. Kundin Alexandra Roth hat das Konto mit der Nummer 15. class Konto(object): def __init__(self, nummer): self.nr = nummer self.stand = 0.0 self.inhaber = None def einzahlen(self, betrag): self.stand = self.stand + betrag def auszahlen(self, betrag): self.stand = self.stand - betrag class Kunde(object): def __init__(self, name, vorname): self.name = name self.vorname = vorname

63 63 Übungen Aufgabe 2 (siehe ) Ein Kunde soll neben einem Erstkonto auch ein Zweitkonto haben können. (a) Beschreibe eine typische Situation in dieser Bankwelt mit einem Objektdiagramm. (b) Beschreibe die Bankwelt auch mit einem Klassendiagramm. (c) Entwickle eine Implementierung zu dieser Bankwelt und ein Testprogramm passend zu Aufgabe (a).

64 64 Übungen Aufgabe 3 (siehe ) In der freien Enzyklopädie Wikipedia werden Artikel zu Stichwörtern von Benutzern verfasst. Eine erste Modellierung der Klassen Artikel und Benutzer könnte wie folgt aussehen: (a) Implementiere die Klassen und erzeuge Objekte zur Beschreibung folgender Situation: * Der Artikel zum Stichwort HTML wurde vom Benutzer helmut03 verfasst. * Der Artikel zum Stichwort CSS wurde vom Benutzer loreley verfasst. * Der Artikel zum Stichwort Barrierefreiheit wurde vom Benutzer loreley verfasst.

65 65 Übungen Aufgabe 3 (siehe ) Das Modell soll so erweitert werden, dass Artikel von Benutzern überarbeitet werden können: (b) Implementiere das erweiterte Modell und erzeuge eine typische Objektsituation. Verdeutliche die erzeugte Objektsituation auch mit einem Objektdiagramm. (c) Wie müsste man die Klasse Benutzer erweitern, wenn hier auch die Mitarbeit an Artikeln mit einem Referenzattribut erfasst werden soll.

66 66 Übungen Aufgabe 4 (siehe ) Ein Geometrieprogramm soll verschiedene geometrische Objekte verwalten. Kannst du passende Klassen konzipieren und implementieren, so dass folgendes Testprogramm sinnvoll ausgeführt werden kann. from geometrie import * # Punkte p1 = Punkt(0, 0) p2 = Punkt(0, 10) p3 = Punkt(10, 10) p4 = Punkt(10, 0) p5 = Punkt(5, 15) # Rechtecke und Dreieck (als n-Eck) rechteck = Rechteck(p1, p3) dreieck = Neck([p2, p3, p5]) # Haus des Nikolaus als Figur haus_nikolaus = Figur() haus_nikolaus.hinzufuegen(rechteck) haus_nikolaus.hinzufuegen(dreieck) # Punkt verschieben und ausgeben p1.verschieben(4, 1)...

67 67 Teil 6 Miniwelt und Datenmodell

68 68 Bankwelt im Computer S Konto-Nr.5Adriana MüllerKontoauszug Blatt 3 DatumErläuterungBetrag Handykosten Zuschuss von Oma und Opa Schuhe Kontostand in EUR, Die Bankwelt mit bestimmten Gegenständen (Kunden, Konten,...) und Vorgängen (Ein-, Auszahlungen,...) soll in einem Programm geeignet erfasst werden.

69 69 Bankwelt im Computer S Konto-Nr.5Adriana MüllerKontoauszug Blatt 3 DatumErläuterungBetrag Handykosten Zuschuss von Oma und Opa Schuhe Kontostand in EUR, Aufgabe: Welche Objekte sind für welche Aufgaben zuständig? Welche Vereinfachungen sind bei dieser Beschreibung einer Bankwelt vorgenommen?

70 70 Bankwelt im Computer from bank import * bank = Bank() # Konten eröffnen bank.kontoEroeffnen("Müller", "Adriana", 2101) bank.kontoEroeffnen("Meier", "Adrian", 1507) bank.kontoEroeffnen("Schuster", "Christiane", 1313) bank.kontoEroeffnen("Weber", "Alexander", 2276) # Ausgabe der konten for kunde in bank.kunden: print("Kunde:") print("Name:", kunde.name) print("Vorname:", kunde.vorname) print("Kontonummer:", kunde.konto.nr) print("Kontostand:", kunde.konto.stand) print() # Transaktion print("Einzahlung") pin = int(input("PIN:")) if bank.existiertPIN(pin): meineKontoNr = bank.getKontoNr(pin) betrag = float(input("Betrag:")) bank.einzahlen(meineKontoNr, betrag)... Aufgabe: Lade dir die Datei bank.py mit einer Implementierung der Klassen herunter. Versuche, mit den Objekten Vorgänge der Bankwelt durchzuspielen. Das folgende Python-Testprogramm zeigt, wie das gehen könnte.

71 71 Miniwelt Mit Miniwelt bezeichnen wir den Weltausschnitt / Gegenstandsbereich, der dem zu entwickelnden Programm (Software-System) zu Grunde liegt. S Konto-Nr.5Adriana MüllerKontoauszug Blatt 3 DatumErläuterungBetrag Handykosten Zuschuss von Oma und Opa Schuhe Kontostand in EUR,

72 72 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.

73 73 Zuständigkeiten Bei der Entwicklung komplexer Software-Systeme ist es günstig, dieses System aus mehreren Objekten zusammenzusetzen. Jedes Objekt sollte dabei für einen bestimmten Aufgabenbereich zuständig sein. Ein solches System aus Objekten mit klar umgrenzten Zuständigkeiten erhöht die Durchschaubarkeit des gesamten Software-Systems und erleichtert es, das System nachträglich abzuändern oder zu erweitern. Objekt der Klasse Kunde: verwaltet die Kundendaten Objekt der Klasse Konto: verwaltet die Kontodaten; führt Ein- und Auszahlungen aus Objekt der Klasse Bank: erzeugt und verwaltet alle Kunde- Objekte und Konto-Objekte; veranlasst alle Geldtransfers zwischen Konten;...

74 74 UML UML steht für uniform modeling language und ist eine normierte Bildsprache, mit der man objektorientierte Modelle beschreiben kann. Sequenzdiagramm Klassendiagramm Objektdiagramm

75 75 UML-Editor Violet ist ein sehr einfaches, frei nutzbares Werkzeug zur Erstellung von UML-Diagrammen. siehe:

76 76 Übungen Aufgabe 1 (siehe ) Beim chuck-a-luck-Spiel soll ein Objekt der Klasse Spiel alle beteiligten Objekte verwalten und die Spielaktionen als Operationen zur Verfügung stellen. (a) Skizziere ein Objektdiagramm zur Miniwelt chuck-a-luck. (b) Woran erkennt man im Klassendiagramm, dass das Spiel- Objekt für die Erzeugung der weiteren Objekte zuständig ist? Welche weiteren Zuständigkeiten hat das Spiel-Objekt? (c) Implementiere dieses Modell. Teste es wie folgt. from chuckaluck import * # Erzeugung der Objekte spiel = Spiel() # Durchführung eines Spiels spiel.einsatzZahlen() spiel.spielzahlSetzen(5) spiel.wuerfelWerfen() spiel.gewinnAuszahlen() # Ausgabe der Spiel print("Würfel A:", spiel.wuerfelA.augen)...

77 77 Übungen Aufgabe 2 (siehe ) Das folgende Klassendiagramm zeigt ein System, mit dem man die Entwicklung einer Population simulieren kann. (a) Implementiere die Klasse Population so, dass die Mäusepopulation aus... (siehe Einstieg - Mäusepopulation) als Miniwelt erfasst wird. (b) Implementiere die Klasse Simulator so, dass sie die Entwicklung der verwalteten Population beschreibt. (c) Erstell ein geeignetes Testprogramm, um die Implementierung zu überprüfen.. (d) Was müsste am Datenmodell verändert werden, wenn man die Population aus... (siehe Miniprojekt) modellieren wollte?

78 78 Übungen Aufgabe 3 (siehe ) Ein Zahlenschloss bestehe aus drei Einstellrädern, mit denen die Ziffern der Schlüsselzahl einzeln eingestellt werden können. Hat man die Schlüsselzahl richtig getroffen, dann ist das Schloss offen. Entwickle ein objektorientiertes Datenmodell, das die vorliegende Miniwelt möglichst strukturgetreu beschreibt. Stell das Datenmodell auch mit Hilfe von UML-Diagrammen dar. Implementiere und teste das Datenmodell.

79 79 Teil 7 Datenmodell und grafische Benutzeroberfläche

80 80 Eine GUI zur Bankwelt Das bisher entwickelte Programm zur Verwaltung von Bankkonten soll um eine grafische Benutzeroberfläche erweitert werden. Der Benutzer soll die Möglichkeit haben, wie an einem Terminal in der Bank seine Bankgeschäfte zu erledigen. Am Terminal kann man sich den aktuellen Kontostand anzeigen lassen, Geldbeträge ein- und auszahlen und auch Überweisungen vorzunehmen.

81 81 Eine GUI zur Bankwelt Aufgabe: Das gezeigte Programm (siehe enthält zwar noch Implementierungslücken, ist aber nichtsdestotrotz lauffähig. Teste zunächst das Programm. Mache dich auch mit dem Quelltext vertaut. Analysiere insbesondere die schon implementierten Ereignisverarbeitungsprozeduren. Implementiere die noch fehlenden Implementierungen. # # Datenmodell # from bank import * bank = Bank() bank.initBank() # # GUI # from tkinter import * #... Prozeduren zur Ereignisverarbeitung # Erzeugung des Fensters fenster = Tk() fenster.title("Bank") fenster.geometry('230x330')...

82 82 Eine GUI zur Bankwelt from tkinter import * class GUIBank(object): def __init__(self, bank): # Referenzattribute zum Datenmodell self.bank = bank # Erzeugung des Fensters self.fenster = Tk() self.fenster.title("Bank") self.fenster.geometry('230x330') # Rahmen PIN self.rahmenPIN = Frame(master=self.fenster, background="#FFCFC9") self.rahmenPIN.place(x=5, y=5, width=220, height=30) # Label mit Aufschrift PIN self.labelPIN = Label(master=self.rahmenPIN, background="white", text="PIN") self.labelPIN.place(x=5, y=5, width=145, height=20) # Entry für die PIN self.entryPIN = Entry(master=self.rahmenPIN) self.entryPIN.place(x=155, y=5, width=60, height=20)... def Button_Anzeigen_Click(self): pin = int(self.entryPIN.get())... Aufgabe: Der folgende Quelltextauszug zeigt eine andere Implementierung der grafischen Benutzeroberfläche. Hier wird ein zusätzliches Objekt benutzt, um sämtliche GUI-Objekte zu verwalten. Analysiere das Programm.

83 83 Eine GUI zur Bankwelt # # Datenmodellobjekte # from bank import * bank = Bank() bank.initBank() # # GUI-Objekte # from guibank import * guibank = GUIBank(bank) Aufgabe: Welche Beziehungen bestehen zwischen den Objekten? Verdeutliche dies mit einem Objekt- und Klassendiagramm.

84 84 Zwei-Komponenten-Architektur # # Datenmodellobjekte # from bank import * bank = Bank() bank.initBank() # # GUI-Objekte # from guibank import * guibank = GUIBank(bank) Das bisher entwickelte System zur Verwaltung von Bankkonten hat eine Zwei-Komponenten- Architektur. Die eine Komponente wird vom Datenmodell gebildet. Diese Komponente ist so konzipiert, dass sie ohne eine grafische Benutzeroberfläche benutzt werden kann. Die andere Komponente umfasst alle Klassen, die für die Erzeugung und Verwaltung der grafischen Benutzeroberfläche benötigt werden. Da Objekte dieser Klassen u. a. für die Darstellung des Datenmodells zuständig sind, dürfen sie Zugriff auf Datenmodell-Objekte haben.

85 85 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.

86 86 Model - View - Control Bisher war das GUI-Objekt sowohl für die Präsentation der Daten auf der Benutzerober- fläche, als auch für die Ereignisverarbeitung zuständig. Diese beiden Zuständigkeiten sollen jetzt aufgeteilt werden: Einerseits soll es Präsentations- objekte geben, die nur für die Darstellung der Daten zuständig sind, andererseits Steuerungs- objekte, die die Verbindungen zwischen Präsentationsobjekten und Datenmodellobjekten herstellen und die Ereignisverarbeitung festlegen. Diese weitere Aufteilung der Zuständigkeiten soll spätere Änderungen oder Erweiterungen des Softwaresystems erleichtern und eine Wiederverwendbarkeit einzelnen Komponenten ermöglichen. Model View Control

87 87 Model - View - Control Model class Konto(object): def __init__(self, nummer): self.nr = nummer self.stand = 0.0 self.inhaber = None... class Kunde(object): def __init__(self, name, vorname, pin): self.name = name self.vorname = vorname self.pin = pin self.konto= None... class Bank(object): def __init__(self): self.konten = [] self.kunden = [] self.naechsteKontoNr = 0...

88 88 Model - View - Control View from tkinter import * class GUIBank(object): def __init__(self, bank, \ cbAnzeigen, cbEinzahlen, cbAuszahlen, cbUeberweisen): # Referenzattribute zum Datenmodell self.bank = bank self.cbAnzeigen = cbAnzeigen self.cbEinzahlen = cbEinzahlen self.cbAuszahlen = cbAuszahlen self.cbUeberweisen = cbUeberweisen # Erzeugung des Fensters self.fenster = Tk() self.fenster.title("Bank") self.fenster.geometry('230x330')... # Button Anzeigen self.buttonAnzeigen = Button(master=self.rahmenKonto, text="anzeigen", \ command=self.cbAnzeigen) self.buttonAnzeigen.place(x=5, y=55, width=145, height=20)... # kein mainloop hier

89 89 Model - View - Control Control from model_bank import * from view_bank import * class Controler(object): def __init__(self): # Erzeugung der Datenmodell-Objekte self.bank = Bank() self.bank.initBank() # Erzeugung und Initialisierung der GUI self.guibank = GUIBank(self.bank, self.buttonAnzeigen, self.buttonEinzahlen,...) # Ereignisschreife self.guibank.fenster.mainloop() def buttonAnzeigen(self): pin = int(self.guibank.entryPIN.get()) if self.bank.existiertPIN(pin): kunde = self.bank.getKunde(pin) self.guibank.labelKontonummer.config(text=str(kunde.konto.nr)) self.guibank.labelKontostand.config(text=str(kunde.konto.stand)) else: self.guibank.labelKontonummer.config(text="") self.guibank.labelKontostand.config(text="")... controler = Controler()

90 90 Model - View - Control Aufgabe eines GUIBank-Objekts ist es, die grafische Benutzeroberfläche zu erzeugen. Model View Control Aufgabe eines Controler-Objekts ist es, die Datenmodell- und GUI-Objekte zu erzeugen und zu verknüpfen. Aufgabe eines Model-Objekts ist es, einen Aspekt der Miniwelt abzubilden.. Beachte, dass ein Controler-Objekt Zugriff auf Model- und View-Objekte hat.

91 91 class GUIBank(object): def __init__(self, bank, cbAnzeigen, cbEinzahlen, cbAuszahlen, cbUeberweisen): # Referenzattribute zum Datenmodell self.bank = bank self.cbAnzeigen = cbAnzeigen self.cbEinzahlen = cbEinzahlen self.cbAuszahlen = cbAuszahlen self.cbUeberweisen = cbUeberweisen # Erzeugung des Fensters self.fenster = Tk() self.fenster.title("Bank") self.fenster.geometry('230x330')... # Button Anzeigen self.buttonAnzeigen = Button(master=self.rahmenKonto, text="anzeigen", \ command=self.cbAnzeigen) self.buttonAnzeigen.place(x=5, y=55, width=145, height=20)... Callback-Funktion Parameter für eine Callback-Funktion Eine Callback-Funktion (bzw. Rückruffunktion) ist eine Funktion, die einer anderen Funktion als Parameter übergeben wird und von dieser unter gewissen Bedingungen aufgerufen wird.

92 92 class Controler(object): def __init__(self): # Erzeugung der Datenmodell-Objekte self.bank = Bank() self.bank.initBank() # Erzeugung und Initialisierung der GUI self.guibank = GUIBank(self.bank, self.buttonAnzeigen, self.buttonEinzahlen,...) # Ereignisschreife self.guibank.fenster.mainloop() def buttonAnzeigen(self): pin = int(self.guibank.entryPIN.get()) if self.bank.existiertPIN(pin): kunde = self.bank.getKunde(pin) self.guibank.labelKontonummer.config(text=str(kunde.konto.nr)) self.guibank.labelKontostand.config(text=str(kunde.konto.stand)) else:... Callback-Funktion Eine Callback-Funktion (bzw. Rückruffunktion) ist eine Funktion, die einer anderen Funktion als Parameter übergeben wird und von dieser unter gewissen Bedingungen aufgerufen wird. Callback-Funktion

93 93 class Uhr(object):... def tick(self): if self.sekunden < 59: self.sekunden = self.sekunden + 1 else: self.sekunden = 0 if self.minuten < 59: self.minuten = self.minuten + 1 else:... Strategie: Befragen Das Objekt guiuhr nutzt eine Strategie, die man Befragen oder Polling nennt. Das Objekt guiuhr weiß genau, wann sich das Datenmodell ändert (nach jedem Anklicken der Schaltfläche) und besorgt sich die geänderten Daten zur Anzeige auf dem vorgesehenen Label. from tkinter import * class GUIUhr(object): def __init__(self, uhr): # Referenzattribute zum Datenmodell self.uhr = uhr # Erzeugung des Fensters... # Button self.buttonTick = Button(..., command=self.Button_Tick_Click)... def Button_Tick_Click(self): # Verarbeitung der Daten self.uhr.tick() # Anzeige der Daten uhrzeit = str(self.uhr.stunden) + ":" + \ str(self.uhr.minuten) + ":" + \ str(self.uhr.sekunden) self.labelUhr.config(text=uhrzeit) Befragen Änderung # Datenmodell from uhr import * uhr = Uhr() # GUI-Objekt from guiuhr import * guiuhr = GUIUhr(uhr) guiuhr.fenster.mainloop()

94 94 import threading, time class Timer(threading.Thread):... class Uhr(object): def __init__(self):... self.aktiv = False self.beobachter = None def setBeobachter(self, beobachter): self.beobachter = beobachter def stellen(self, h, m, s): self.stunden.setzen(h) self.minuten.setzen(m) self.sekunden.setzen(s) self.beobachter.anzeigeAktualisieren() Strategie: Beobachten Das Objekt guiuhr nutzt hier eine Strategie, die man Beobachten nennt. Das Objekt guiuhr wird als registriertes beobachtendes Objekt jeweils benachrichtigt, wenn sich das Datenmodell verändert hat.... def stoppen(self): self.aktiv = False def tick(self): if self.sekunden < 59: self.sekunden = self.sekunden + 1 else: self.sekunden = 0... self.beobachter.anzeigeAktualisieren() if self.aktiv: self.timer = Timer(1, self.tick) self.timer.start() def ticken(self): self.aktiv = True self.timer = Timer(1, self.tick) self.timer.start() Beobachter benachrichtigen Änderung

95 95 from tkinter import * class GUIUhr(object): def __init__(self, uhr): # Referenzattribute zum Datenmodell self.uhr = uhr... def Button_Start_Click(self): # Verarbeitung der Daten self.uhr.ticken() def Button_Stopp_Click(self): # Verarbeitung der Daten self.uhr.stoppen() def anzeigeAktualisieren(self): # Anzeige der Daten uhrzeit = str(self.uhr.stunden)+":"+ str(self.uhr.minuten)+":" +str(self.uhr.sekunden) self.labelUhr.config(text=uhrzeit) Strategie: Beobachten Beachte, dass die Klassen Uhr und GUIUhr so gestaltet sind, dass die Trennung zwischen Datenmodell und GUI weiterhin Bestand hat. Erst zur Laufzeit wird der Beobachter des Datenmodellobjekts festgelegt. # Datenmodell from uhr import * uhr = Uhr() # GUI-Objekt from guiuhr import * guiuhr = GUIUhr(uhr) # Beobachter festlegen uhr.setBeobachter(guiuhr) # Ereignisschleife starten guiuhr.fenster.mainloop()

96 96 Übungen Aufgabe 1 (siehe ) Die Klasse ModuloZaehler beschreibe Zähler, die modulo einer vorgegebenen Zahl zählen. Entwickle eine einfache grafische Benutzeroberfläche, mit der man das Verhalten eines Modulo-Zählers verdeutlichen kann.

97 97 Übungen Aufgabe 2 (siehe ) Ziel ist es, ein objektorientieres Datenmodell mit einer grafische Benutzeroberfläche zu verknüpfen. Als Miniwelt betrachten wir das Spiel chuck a luck.

98 98 Übungen Aufgabe 2 (siehe ) Für die Benutzeroberfläche kannst du eine bereits entwickelte Implementierung übernehmen. Implementiere das gesamte Programm so, dass eine klare Trennung zwischen Datenmodell und GUI erkennbar ist.

99 99 Teil 6 Vererbung

100 100 Sparkonto Ein Sparkonto ist ein Konto, bei dem das eingezahlte Geld mit einem bestimmten, mit der Bank vereinbarten Zinssatz verzinst wird. Das folgende Klassendiagramm zeigt ein Sparkonto im Vergleich zu einem normalen Konto. Aufgabe: Vergleiche die Klassendiagramme. Inwiefern unterscheiden sich die Attribute und Methoden der beiden Klassen? Überleg dir auch, ob sich bestimmte Methoden evtl. anders verhalten. Beachte, dass man das den Klassendiagrammen nicht entnehmen kann. Hinweis: Welche Regelungen gibt es bei Auszahlungen von einem Sparkonto?

101 101 Sparkonto Ein Sparkonto kann als spezielles Konto angesehen werden, das - im Vergleich zu einem herkömmlichen Konto - nur in einigen Bestandteilen abgeändert worden ist. Aufgabe: Wie würde man ein solches Klassendiagramm wohl lesen?

102 102 Sparkonto Aufgabe: Analysiere die Implementierung der Klasse Sparkonto. Alles klar? Teste auch diese Implementierung. class Konto(object): def __init__(self, nummer): self.nr = nummer self.stand = 0 self.minimum = def einzahlen(self, betrag): self.stand = self.stand + betrag def auszahlen(self, betrag): if self.stand - betrag >= self.minimum: self.stand = self.stand - betrag else: print("Auszahlung nicht möglich!") class Sparkonto(Konto): def __init__(self, nummer): Konto.__init__(self, nummer) self.zinssatz = None self.minimum = 0 self.maxAuszahlung = def setZinssatz(self, zinssatz): self.zinssatz = zinssatz def auszahlen(self, betrag): if betrag <= self.maxAuszahlung: if self.stand - betrag >= self.minimum: self.stand = self.stand - betrag else: print("Auszahlung nicht möglich!") else: print("Auszahlung nicht möglich!") def zinsenGutschreiben(self): zinsen = self.stand * (self.zinssatz / 100) self.einzahlen(zinsen)

103 103 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 Basisklasse oder Superklasse. Subklasse Basisklasse / Superklasse Ü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.

104 104 Implementierung in Python class Konto(object): def __init__(self, nummer): self.nr = nummer self.stand = 0 self.minimum = def einzahlen(self, betrag): self.stand = self.stand + betrag def auszahlen(self, betrag): if self.stand - betrag >= self.minimum: self.stand = self.stand - betrag else: print("Auszahlung nicht möglich!") class Sparkonto(Konto): def __init__(self, nummer): Konto.__init__(self, nummer) self.zinssatz = None self.minimum = 0 self.maxAuszahlung = def setZinssatz(self, zinssatz): self.zinssatz = zinssatz def auszahlen(self, betrag): if betrag <= self.maxAuszahlung: if self.stand - betrag >= self.minimum: self.stand = self.stand - betrag else: print("Auszahlung nicht möglich!") else: print("Auszahlung nicht möglich!") def zinsenGutschreiben(self): zinsen = self.stand * (self.zinssatz / 100) self.einzahlen(zinsen) Vererbung Überschreiben Ergänzen Übernehmen

105 105 Übungen Aufgabe (siehe und ) Auf den folgenden Folien werden verschiedene Implementierungen einer grafischen Benutzeroberfläche gezeigt. Analysiere und erkläre, was in den jeweiligen Version unterschiedlich implementiert wird.

106 106 # Datenmodell from wuerfel import * wuerfel = Wuerfel() # GUI from tkinter import * # Ereignisverarbeitung def Wuerfel_Click(): # Verarbeitung der Daten wuerfel.werfen() # Anzeige der Daten labelWuerfel.config(text=str(wuerfel.augen)) # Erzeugung des Fensters fenster = Tk() fenster.title("Würfel") fenster.geometry('120x110') # Eingabefeld für die Zahl labelWuerfel = Label(master=fenster, text="1", background="#FBD975") labelWuerfel.place(x=45, y=40, width=30, height=30) # Button zum Auswerten buttonWuerfel = Button(master=fenster, text="Würfel werfen", command=Wuerfel_Click) buttonWuerfel.place(x=10, y=80, width=100, height=20) # Aktivierung des Fensters fenster.mainloop() Übungen from random import randint class Wuerfel(object): def __init__(self): self.augen = 1 def werfen(self): self.augen = randint(1, 6)

107 107 from tkinter import * class GUIWuerfel(object): def __init__(self, wuerfel): # Referenzattribute zum Datenmodell self.wuerfel = wuerfel # Erzeugung des Fensters self.fenster = Tk() self.fenster.title("Bank") self.fenster.geometry('120x110') # Eingabefeld für die Zahl self.labelWuerfel = Label(master=self.fenster, text="1", background="#FBD975") self.labelWuerfel.place(x=45, y=40, width=30, height=30) # Button zum Auswerten self.buttonWuerfel = Button(master=self.fenster, text="Würfel werfen", command=self. Wuerfel_Click) self.buttonWuerfel.place(x=10, y=80, width=100, height=20) def Wuerfel_Click(self): # Verarbeitung der Daten self.wuerfel.werfen() # Anzeige der Daten self.labelWuerfel.config(text=str(self.wuerfel.augen)) Übungen # Datenmodell from wuerfel import * wuerfel = Wuerfel() # GUI-Objekt from guiwuerfel import * guiwuerfel = GUIWuerfel(wuerfel) guiwuerfel.fenster.mainloop()

108 108 from tkinter import * class GUIWuerfel(Tk): def __init__(self, wuerfel): # Referenzattribute zum Datenmodell Tk.__init__(self) self.wuerfel = wuerfel # Erzeugung des Fensters self.title("Würfel") self.geometry('120x110') # Eingabefeld für die Zahl self.labelWuerfel = Label(master=self, text="1", background="#FBD975") self.labelWuerfel.place(x=45, y=40, width=30, height=30) # Button zum Auswerten self.buttonWuerfel = Button(master=self, text="Würfel werfen", command=self.Button_Wuerfel_Click) self.buttonWuerfel.place(x=10, y=80, width=100, height=20) def Button_Wuerfel_Click(self): # Verarbeitung der Daten self.wuerfel.werfen() # Anzeige der Daten self.labelWuerfel.config(text=str(self.wuerfel.augen)) Übungen # Datenmodell from wuerfel import * wuerfel = Wuerfel() # GUI-Objekt from guiwuerfel import * guiwuerfel = GUIWuerfel(wuerfel) guiwuerfel.mainloop()

109 109 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 Helmut Balzert: Lehrbuch Grundlagen der Informatik. Spektrum Ak. Verlag Bernd Oestereich: Objektorientierte Softwareentwicklung. Oldenbourg 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 P. Damann, J. Wemßen: Objektorientierte Programmierung mit Delphi, Band 2. Klett-Verlag 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....

110 110 Literaturhinweise Im Internet findet man ebenfalls sehr viele schulgerechte Darstellungen der objektorientierten Modellierung und Programmierung, z. B: Die AG-Informatik des LMZ in RLP stellt u. a. auch Fortbildungsmaterialien zu diesem Thema bereit. 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:


Herunterladen ppt "Konzepte objektorientierter Programmierung Klaus Becker 2009."

Ähnliche Präsentationen


Google-Anzeigen