Konzepte objektorientierter Programmierung Objekte und Klassen Klaus Becker
Konzepte objektorientierter Programmierung: Objekte und Klassen Klaus Becker 2019
2 Spiele am Computer "Objektorientierung ist die derzeitige Antwort auf die gestiegene Komplexität der Softwareentwicklung. " Oestereich: Objektorientierte Software-Entwicklung Wir werden hier einfache Spielprogramme mit Hilfe von Softwarebausteinen entwickeln. Dabei sollen die Grundideen und Fachkonzepte der objektorientierten Programmierung im Vordergrund stehen und Schritt für Schritt entwickelt werden.
3 Teil 1 Objekte als Softwarebausteine
Das Spiel 17 -und-4 4 Wir spielen 17 -und-4 hier in einer vereifachten Version. Man benötigt hierzu einen Kartenstapel, bei dem die Karten gut durchmischt sind. Die Spieler können jetzt (reihum) Karten ziehen. Ziel ist es, mit den gezogenen Karten der Zahl 21 - also 17 und 4 - möglichst nahe zu kommen. Wer mit den gezogenen Karten mehr als 21 erzielt, hat verloren. Jede Karte hat dabei einen festgelegten Kartenwert: 11 10 9 8 7 4 3 2 Ziel ist es, das Spiel 17 -und-4 mit Hilfe geeigneter Softwarebausteine zu simulieren.
Ein Kartenstapel 5 Für die Simulation des Spiels 17 -und-4 wäre es günstig, wenn man einen Softwarebaustein hätte, der sich genauso verhält wie ein realer Kartenstapel. mischen Karte ziehen ist leer? vorhandene Karten gezogene Karte Ein Kartenstapel besteht aus einer Ansammlung noch vorhandener Karten (und ggf. einer gezogenen Karte). Einen Kartenstapel kann man mischen. Wenn er nicht leer ist, dann kann man die oberste Karte ziehen.
Ein Kartenstapel 6 mischen Karte ziehen ist leer? vorhandene Karten gezogene Kartenstapel-Objekt Ein Softwareobjekt zur Simulation eines Kartenstapels muss also die noch vorhandenen Karten verwalten können. Zudem muss es Operationen zur Verarbeitung der verwalteten Daten vorsehen, die den Operationen eines realen Kartenstapels entsprechen: - Karten mischen - überprüfen, ob überhaupt noch Karten vorhanden sind - Karte ziehen
7 Experimente mit Kartenstapel-Objekten #-----------------------------# Kartenstapel #-----------------------------from random import randint class Kartenstapel(object): … Bauplan für Kartenstapel-Objekte Kartenstapel-Objekt Wir stellen hier einen Bauplan zur Erzeugung von Kartenstapel-Softwareobjekten zur Verfügung. Du musst die Details des Bauplans nicht verstehen, um die nach dem Bauplan erzeugten Softwareobjekte nutzen zu können. Wir werden uns in einem der folgenden Abschnitte genauer mit solchen Bauplänen beschäftigen. Den Bauplan zur Erzeugung von Kartenstapel-Softwareobjekten findest du in der Datei kartenstapel. py. Lade diese Datei herunter und öffne sie mit dem Python-Editor. Führe den Quelltext mit [Run][Run Module] einmal aus. Beachte, dass beim Ausführen die im Quelltext vorliegende Klassendeklaration vom Python-Ausführsystem übernommen wird. Es wird jedoch kein Ergebnis im Ausführfenster angezeigt. Jetzt kann die Simulation des Kartenstapels beginnen.
8 Experimente mit Kartenstapel-Objekten >>> kartenstapel = Kartenstapel() >>> kartenstapel <__main__. kartenstapel object at …> >>> kartenstapel. karten. Liste ['X-A', 'X-K', 'X-D', 'X-B', 'X-10', 'X-9', 'X-8', 'X-7', 'P-A', 'P-K', 'P-D', 'P-B', 'P-10', 'P-9', 'P-8', 'P-7', 'H-A', 'H-K', 'H-D', 'H-B', 'H-10', 'H-9', 'H-8', 'H-7', 'K-A', 'K-K', 'K-D', 'K-B', 'K-10', 'K-9', 'K-8', 'K-7'] >>> kartenstapel. mischen() >>> kartenstapel. karten. Liste ['X-9', 'P-10', 'P-D', 'X-8', 'X-A', 'H-10', 'P-7', 'K-10', 'X-7', 'H-D', 'P-9', 'K-7', 'K-9', 'X-K', 'X-10', 'K-A', 'P-B', 'P-K', 'H-A', 'K-K', 'P-8', 'K-B', 'P-A', 'H-7', 'H-9', 'K-8', 'X-B', 'H-K', 'K-D', 'H-B', 'H-8'] >>> kartenstapel. ist. Leer() False >>> kartenstapel. karte. Ziehen() 'X-9' >>> kartenstapel. karten. Liste ['P-10', 'P-D', 'X-8', 'X-A', 'H-10', 'P-7', 'K 10', 'X-7', 'H-D', 'P-9', 'K-7', 'K-9', 'X-K', 'X-10', 'K-A', 'P-B', 'P-K', 'H-A', 'K-K', 'P-8', 'K-B', 'P-A', 'H-7', 'H-9', 'K-8', 'X-B', 'H-K', 'K-D', 'H-B', 'H-8'] Aufgaben: Analysiere den Python-Dialog und beantworte die folgenden Fragen: - Wie wird das Kartenstapel-Objekt erzeugt? - Wie inspiziert man die vom Kartenstapel-Objekt verwalteten Daten? - In welcher Reihenfolge liegen die Karten zu Beginn auf dem Kartenstapel? - Was bewirkt die Anweisung kartenstapel. mischen()? - Wie wird im Python-Dialog eine Karte "gezogen"? Welche Karte ist es hier? Führe selbst einen solchen Python. Dialog aus. Erzeuge zunächst ein Objekt kartenstapel. Führe mit diesem Objekt ein 17 -und-4 -Spiel (mit einem einzigen Spieler) durch. Achte genau auf die Darstellung der Details.
9 Experimente mit Kartenstapel-Objekten from kartenstapel import Kartenstapel # Testprogramm kartenstapel = Kartenstapel() print(kartenstapel. karten. Liste) print() kartenstapel. mischen() print(kartenstapel. karten. Liste) print(kartenstapel. ist. Leer()) gezogene. Karte = kartenstapel. karte. Ziehen() print(gezogene. Karte) print(kartenstapel. karten. Liste) Aufgabe: Die Verwendung eines Kartenstapel. Objekts kann man auch mit einem Testprogramm erkunden. Speichere das Testprogramm im selben Ordner ab, in dem auch die Datei kartenstapel. py mit der Klassendeklaration der Klasse Kartenstapel gespeichert ist. Führe das Testprogramm anschließend aus und erläutere die erzeugten Ausgaben.
10 Experimente mit Kartenstapel-Objekten Aufgabe: Entwickle analog ein Testprogramm, das einen Kartenstapel erzeugt, die Karten mischt und anschließend solange die oberste Karte zieht und ausgibt, bis der Kartenstapel leer ist. Benutze dabei die Operationen, die für Kartenstapel-Objekte vorgesehen sind (siehe Objektdiagramm).
11 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 einem Kartenstapel (als Objekt der realen Welt) wird ein Softwareobjekt kartenstapel konzipiert. Das folgende Ablaufprotokoll verdeutlicht die Arbeitsweise eines Softwareobjekts kartenstapel: Das Softwareobjekt verwaltet Daten - im vorliegenden Fall einen Stapel von Karten. Das Softwareobjekt nutzt hierzu die interne Variable karten. Liste zur Verwaltung einer Liste von Zeichenketten. Das Softwareobjekt kartenstapel stellt auch Operationen zur Verarbeitung der verwalteten Daten bereit. Im vorliegenden Fall sind das die Operationen mischen(), ist. Leer() und karte. Ziehen(). Das Softwareobjekt ist also eine Einheit, die Daten verwaltet und Operationen zur Verarbeitung der verwalteten Daten zur Verfügung stellt. Statt von Softwareobjekten wird im Folgenden kurz von Objekten gesprochen.
13 Fachkonzept - Objekt Ein Objekt ist eine autonome Software. Einheit, die für bestimmte Aufgaben zuständig ist. Ein Objekt kann Daten mit Hilfe von Attributen verwalten und Operationen zur Verarbeitung der verwalteten Daten mit Hilfe von Methoden ausführen. Attribute sind - an Objekte gebundene Variablen zur Verwaltung von Daten. Diese entsprechen den Eigenschaften des betreffenden Objekts. Methoden sind - an Objekte gebundene Funktionen zur Verarbeitung von Daten. Diese Methoden stellen die Fähigkeiten des betreffenden Objekts dar. 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
14 Fachkonzept - Objekt Zugriff auf Attribute: Objekt objekt. attribut kartenstapel. karten. Liste Aktivierung von Methoden: objekt. methode kartenstapel. mischen() Attribute Attributwerte Ausführung einer Methode Objektdiagramm
15 Übungen Aufgabe 1: Ziel ist es, Würfel mit Softwareobjekten zu simulieren. Einen Bauplan zur Erzeugung von entsprechenden Softwareobjekten findest du in der Datei wuerfel. py. Öffne diese Datei im Python. Editor und führe den Quelltext mit [Run][Run Module] einmal aus. Beachte, dass beim Ausführen die im Quelltext vorliegende Klassendeklaration vom Python-Ausführsystem übernommen wird. Es wird jedoch kein Ergebnis im Ausführfenster angezeigt. Ergänze das Objektdiagramm so, dass es die Objektkonstallation am Ende des Python-Dialogs korrekt beschreibt. >>> 2 >>> 1 >>> 4 >>> 5 >>> 2 w 1 = Wuerfel() w 1. augen w 2 = Wuerfel() w 2. augen w 3 = Wuerfel() w 3. augen w 1. werfen() w 1. augen w 3. werfen() w 3. augen
16 Übungen Aufgabe 2: Mit einem Programm soll die folgende Objektkonstellation erzeugt werden. Ergänze das Programmfragment und teste es. from wuerfel import Wuerfel # Testprogramm w 1 = Wuerfel() w 2 = Wuerfel(). . . while w 1. augen != 6: . . . print(w 1. augen)
17 Teil 2 Klassen als Baupläne für Objekte
18 Ein Bauplan für Kartenstapel-Objekte from random import randint class Kartenstapel(object): def __init__(self): self. karten. Liste = ['X-A', 'X-K', 'X-D', 'X-B', 'X-10', 'X-9', 'X-8', 'X-7', 'P-A', 'P-K', 'P-D', 'P-B', 'P-10', 'P-9', 'P-8', 'P-7', 'H-A', 'H-K', 'H-D', 'H-B', 'H-10', 'H-9', 'H-8', 'H-7', 'K-A', 'K-K', 'K-D', 'K-B', 'K-10', 'K-9', 'K-8', 'K-7'] def mischen(self): neue. Liste = [] aktuelle. Anzahl = len(self. karten. Liste) while aktuelle. Anzahl > 0: i = randint(0, aktuelle. Anzahl-1) neue. Liste = neue. Liste + [self. karten. Liste[i]] del self. karten. Liste[i] aktuelle. Anzahl = len(self. karten. Liste) self. karten. Liste = neue. Liste def ist. Leer(self): if len(self. karten. Liste) > 0: ergebnis = False else: ergebnis = True return ergebnis def karte. Ziehen(self): if len(self. karten. Liste) > 0: gezogene. Karte = self. karten. Liste[0] self. karten. Liste = self. karten. Liste[1: ] else: gezogene. Karte = None return gezogene. Karte mischen Karte ziehen ist leer? vorhandene Karten gezogene Karte
19 Ein Kartenhaufen Was macht einen Kartenhaufen aus? Karte hinzufügen Kartenliste Wert Unter einem Kartenhaufen soll hier eine Ansammlung von Karten verstanden werden. Einem solchen Kartenhaufen kann man weitere Karten hinzufügen. Ein Kartenhaufen hat zudem einen Gesamtwert (das ist die Summe der Werte aller Karten des Kartenhaufens). Im vorliegenden Beispiel beträgt der Gesamtwert 23.
20 Simulation eines Kartenhaufens >>> mein. Kartenhaufen = Kartenhaufen() >>> mein. Kartenhaufen. hinzufuegen('H-D') >>> mein. Kartenhaufen. hinzufuegen('X-A') >>> mein. Kartenhaufen. hinzufuegen('K-9') >>> mein. Kartenhaufen. karten. Liste ['H-D', 'X-A', 'K-9'] >>> mein. Kartenhaufen. wert 23 So soll es funktionieren! Karte hinzufügen Kartenliste Wert
21 Ein Bauplan für Kartenhaufen-Objekte class Kartenhaufen(object): def __init__(self): self. karten. Liste = #. . . self. wert = #. . . def hinzufuegen(self, karte): self. karten. Liste = #. . . if karte[2] == 'A': kartenwert = 11 elif karte[2] == 'K': kartenwert = 4 elif karte[2] == 'D': kartenwert = 3 elif karte[2] == 'B': kartenwert = 2 elif karte[2] == '1': kartenwert = 10 #. . . self. wert = #. . . >>> mein. Kartenhaufen = Kartenhaufen(). . . Aufgabe: Die Klassendeklaration ist noch nicht ganz fertig. Kannst du die fehlenden Teile (mit #. . . markiert) ergänzen? Wenn du die Klassendeklaration richtig ergänzt hast, dann kannst du als Test einen geeigneten Python-Dialog führen. Vorher musst du die Klassendeklaration aber einmal ausführen, damit sie vom Python. Ausführsystem übernommen wird.
Aufgabe 22 Aufgabe: Speichere die Klassendeklarationen der Klassen Kartenstapel und Kartenhaufen in einer gemeinsamen Datei ab und führe sie einmal aus. Simuliere anschließend ein 17 -und-4 -Spiel mit zwei Spielern. Hier der Beginn einer solchen Simulation. >>> >>> >>> 10 >>> 3 >>> kartenstapel = Kartenstapel() kartenstapel. mischen() kartenhaufen. Spieler 1 = Kartenhaufen() kartenhaufen. Spieler 2 = Kartenhaufen() karte = kartenstapel. karte. Ziehen() kartenhaufen. Spieler 1. hinzufuegen(karte) karte = kartenstapel. karte. Ziehen() kartenhaufen. Spieler 2. hinzufuegen(karte) kartenhaufen. Spieler 1. wert kartenhaufen. Spieler 2. wert. . .
23 Aufgabe: In der Datei kartenspiel. py befinden sich die Deklarationen der Klassen Kartenstapel und Kartenhaufen. Analysiere das Testprogramm. Stelle eine Vermutung auf, was es leistet. Überprüfe anschließend deine Vermutung. Das Testprogramm muss sich im selben Verzeichnis wie die Datei kartenspiel. py befinden. from kartenspiel import Kartenstapel, Kartenhaufen # Testprogramm kartenstapel = Kartenstapel() kartenstapel. mischen() kartenhaufen. Spieler 1 = Kartenhaufen() kartenhaufen. Spieler 2 = Kartenhaufen() print('Spieler 1: ') karte = kartenstapel. karte. Ziehen() print(karte) kartenhaufen. Spieler 1. hinzufuegen(karte) print(kartenhaufen. Spieler 1. wert) print('Spieler 2: ') karte = kartenstapel. karte. Ziehen() print(karte) kartenhaufen. Spieler 2. hinzufuegen(karte) print(kartenhaufen. Spieler 2. wert) print()
24 Aufgabe: In der Datei kartenspiel. py befinden sich die Deklarationen der Klassen Kartenstapel und Kartenhaufen. (a) Ein Spieler zieht seine Karten nach der folgenden Strategie: Solange der Gesamtwert aller Karten noch kleiner als 18 ist, wird eine Karte gezogen. Entwickle ein Simulationsprogramm zu dieser Strategie. (b) Ein Spieler zieht seine Karten immer nach der folgenden Strategie: Solange der Kartenwert noch kleiner als 18 ist, wird eine Karte gezogen. Ermittle mit einer Simulation, wie oft der Spieler bei dieser Strategie im Mittel über der 21 landet. Zusatz: Das Ziehen von zwei Assen soll dabei nicht mitgezählt werden.
25 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. 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. 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.
26 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. 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 Objektdiagramm
27 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 Konstruktormethode keine Methode ist, die ein Objekt ausführen kann. Destruktormethoden werden in der Regel in Klassendiagrammen weggelassen.
28 Klassendeklaration in Python Klassenname Oberklasse Schlüsselwort class Kartenhaufen(object): def __init__(self): self. karten. Liste = [] Attribute self. wert = 0 Einrückung Doppelpunkt Konstruktor Methode def hinzufuegen(self, karte): self. karten. Liste = self. karten. Liste + [karte] if karte[2] == 'A': kartenwert = 11 Referenz auf Objekt elif karte[2] == 'K': kartenwert = 4 elif karte[2] == 'D': kartenwert = 3 elif karte[2] == 'B': kartenwert = 2 elif karte[2] == '1': kartenwert = 10. . . self. wert = self. wert + kartenwert
29 Objekterzeugung in Python >>> mein. Kartenhaufen = Kartenhaufen() >>> mein. Kartenhaufen <__main__. Kartenhaufen object at 0 x 0136 C 4 B 0> >>> mein. Kartenhaufen. __dict__ {'wert': 0, 'karten. Liste': []} >>> mein. Kartenhaufen = Kartenhaufen() >>> mein. Kartenhaufen <__main__. Kartenhaufen object at 0 x 013 ADFB 0> >>> del mein. Kartenhaufen >>> mein. Kartenhaufen Traceback (most recent call last): File. . . ampel 1 Name. Error: name 'mein. Kartenhaufen' is not defined >>> Erzeugung eines Objekts Inspektion eines Objekts Vernichtung eines Objekts
30 Experimente mit dem Python-Tutor Objekterzeugung in Python
Übungen 31 class Kartenhand(object): def __init__(self): self. karten. Liste = [] self. anzahl = 0 def hinzufuegen(self, karte): self. karten. Liste = self. karten. Liste + [karte] self. anzahl = self. anzahl + 1 def wegnehmen(self, karte): if karte in self. karten. Liste: i = self. karten. Liste. index(karte) del self. karten. Liste[i] self. anzahl = self. anzahl - 1 Aufgabe 1: (a) Analysiere zunächst die Klasse Kartenhand. Welche Attribute werden hier festgelegt? Was sollen sie verwalten? Welche Methoden gibt es? Was sollen sie leisten? Beschreibe die Klasse auch mit einem Klassendiagramm. >>> >>> ? >>> >>> ? meine. Karten = Kartenhand() meine. Karten. hinzufuegen('P-B') meine. Karten. karten. Liste meine. Karten. hinzufuegen('H-10') meine. Karten. karten. Liste meine. Karten. hinzufuegen('P-A') meine. Karten. karten. Liste meine. Karten. wegnehmen('H-10') meine. Karten. karten. Liste meine. Karten. wegnehmen('X-7') meine. Karten. karten. Liste meine. Karten. anzahl (b) Was steht an Stelle der Fragezeichen? Überprüfe deine Vermutungen.
Übungen 32 class Kartenhand(object): def __init__(self, karten. Gegeben): self. karten. Liste = karten. Gegeben self. anzahl = len(self. karten. Liste) def hinzufuegen(self, karte): self. karten. Liste = self. karten. Liste + [karte] self. anzahl = self. anzahl + 1 >>> meine. Karten = Kartenhand(['X-A', 'H-A', 'P-7']) >>> meine. Karten. karten. Liste ['X-A', 'H-A', 'P-7'] >>> meine. Karten. anzahl 3 def wegnehmen(self, karte): if karte in self. karten. Liste: i = self. karten. Liste. index(karte) del self. karten. Liste[i] self. anzahl = self. anzahl - 1 Aufgabe 1: (c) Die Deklaration der Klasse Kartenhand unterscheidet sich von der oben gezeigten nur im Kontruktor. Was ist hier anders? Wozu könnte das gut sein? Betrachte hierzu auch den Python. Dialog.
Übungen 33 class Kartenhand(object): def __init__(self, karten. Gegeben): self. karten. Liste = karten. Gegeben self. anzahl = len(self. karten. Liste) def existiert. Karte(self, karte): if karte in self. karten. Liste: ergebnis = True else: ergebnis = False return ergebnis def hinzufuegen(self, karte): if not self. existiert. Karte(karte): self. karten. Liste = self. karten. Liste + [karte] self. anzahl = self. anzahl + 1 def wegnehmen(self, karte): if self. existiert. Karte(karte): i = self. karten. Liste. index(karte) del self. karten. Liste[i] self. anzahl = self. anzahl - 1 Aufgabe 1: (d) Erläutere die Unterschiede zu den bisher betrachteten Klassendeklarationen. Führe selbst einen Python-Dialog zum Testen aus. Dokumentiere mit diesem Dialog das Verhalten der einzelnen Methoden der Klasse.
34 Übungen Aufgabe 2: Gegeben ist ein Klassendiagramm zur Beschreibung einer Klasse Wuerfel. (a) Erläutere die Bestandteile der Klasse Wuerfel: Welche Daten werden hier verwaltet? Welche Operationen soll ein Wuerfel-Objekt ausführen können? (b) Implementiere zunächst die Klasse Wuerfel. Entwickle dann einen Python-Dialog zum Testen dieser Klasse. In diesem Dialog sollen zwei Würfelobjekte erzeugt und geworfen werden. (c) Ermittle mit einem Testprogramm, wie oft man einen Würfel (durchschnittlich) werfen muss, bis die Summe der Augenzahlen der Würfelwürfe die Zahl 21 überschreitet. Benutze die Klasse Wuerfel, um ein Würfelobjekt zu erzeugen.
35 Übungen Aufgabe 3: Das Spiel 17 und 4 soll hier mit einem Dodekaederwürfel gespielt werden. Ein solcher Würfel ist ein Körper mit 12 regelmäßigen Fünfecken als Flächen. Gegeben ist ein Klassendiagramm mit einem Bauplan für Dodekaederwürfel: (a) Erläutere die Bestandteile der Klasse Dodekaederwuerfel: Welche Attribute und Methoden sind vorgesehen? Wofür sind sie im vorliegenden Kontext gedacht? (b) Implementiere zunächst die Klasse Dodekaederwuerfel. Entwickle dann einen Python-Dialog zum Testen dieser Klasse. (c) Ermittle mit einem Testprogramm, wie oft man einen Dodekaederwürfel (durchschnittlich) werfen muss, bis die Summe der Augenzahlen der Würfelwürfe die Zahl 21 überschreitet. Benutze die Klasse Dodekaederwuerfel, um ein Würfelobjekt zu erzeugen.
36 Übungen Aufgabe 4: Für viele Spiele benötigt man eine Art Punktekonto. Auf dieses Konto kann man eine vorgegebene Anzahl von Punkten "einzahlen", der Punktekontostand erhöht sich dann entsprechen. Man kann analog auch eine vorgegebene Anzahl von Punkten "abheben", natürlich nur, wenn genügend Punkte auf den Konto sind. Entwickle zunächst ein Klassendiagramm mit allen benötigten Attributen und Methoden. Entwickle anschließend eine passende Implementierung und teste sie.
37 UML steht für "uniform modeling language" und ist eine normierte Bildsprache, mit der man objektorientierte Modelle beschreiben kann. UML stellt zur Beschreibung von Modellen verschiedene Diagrammtypen zur Verfügung: Objektdiagramme, Klassendiagramme, … Klassendiagramm Objektdiagramm
38 UML-Editor Violet is a UML editor with these benefits: Ø It is very easy to learn and use Ø It draws nice-looking diagrams of the most commonly used types (class, sequence, etc. ) Ø It is completely free (includes source, distributed under the GNU General Public License) Ø It is cross-platform Quelle: http: //www. horstmann. com/violet/
39 Teil 3 Datenkapselung bei Objekten
40 Experimente mit Kartenhaufen-Objekten class Kartenhaufen(object): def __init__(self): self. karten. Liste = [] self. wert = 0 def hinzufuegen(self, karte): self. karten. Liste = self. karten. Liste + [karte] if karte[2] == 'A': kartenwert = 11 elif karte[2] == 'K': kartenwert = 4 elif karte[2] == 'D': kartenwert = 3 elif karte[2] == 'B': kartenwert = 2 elif karte[2] == '1': kartenwert = 10 elif karte[2] == '9': kartenwert = 9 elif karte[2] == '8': kartenwert = 8 … self. wert = self. wert + kartenwert
41 Experimente mit Kartenhaufen-Objekten >>> mein. Kartenhaufen = Kartenhaufen() >>> mein. Kartenhaufen. karten. Liste = ['H-K'] >>> mein. Kartenhaufen. hinzufuegen('K-B') >>> mein. Kartenhaufen. hinzufuegen('X-9') >>> mein. Kartenhaufen. karten. Liste ['H-K', 'K-B', 'X-9'] >>> mein. Kartenhaufen. wert 11 Aufgabe: (a) Welcher reale Vorgang wird hier simuliert? (b) Stimmt der angegebene Gesamtwert der Karten? Warum ist hier etwas schiefgelaufen?
42 Experimente mit Kartenhaufen-Objekten >>> mein. Kartenhaufen = Kartenhaufen() >>> mein. Kartenhaufen. set. Karten. Liste( ['H-K']) >>> mein. Kartenhaufen. hinzufuegen('K-B') >>> mein. Kartenhaufen. hinzufuegen('X-9') >>> mein. Kartenhaufen. get. Karten. Liste() ['H-K', 'K-B', 'X-9'] >>> mein. Kartenhaufen. get. Wert() 15 Kein direkter Zugriff auf die Attribute vorgesehen Aufgabe: Auf welche Weise wird hier der Zugriff auf die Attributwerte realisiert?
43 Experimente mit Kartenhaufen-Objekten class Kartenhaufen(object): def __init__(self): self. karten. Liste = [] self. wert = 0 Strategie: Kein Zugriff auf Attribute def kartenwert(self, karte): … def hinzufuegen(self, karte): … def get. Karten. Liste(self): return self. karten. Liste def get. Wert(self): return self. wert Klasse mit Zugriffsmethoden Strategie: Zugriff auf Attribute nur mit Zugriffsmethoden def set. Karten. Liste(self, vorgegebene. Karten): self. karten. Liste = vorgegebene. Karten gesamtwert = 0 for karte in self. karten. Liste: gesamtwert = gesamtwert + self. kartenwert(karte) self. wert = gesamtwert
44 >>> >>> ? >>> ? Experimente mit Kartenhaufen-Objekten mein. Kartenhaufen = Kartenhaufen() mein. Kartenhaufen. set. Karten. Liste(['K-B', 'H-D']) mein. Kartenhaufen. get. Karten. Liste() mein. Kartenhaufen. get. Wert() mein. Kartenhaufen. hinzufuegen('X-7') mein. Kartenhaufen. hinzufuegen('H-K') mein. Kartenhaufen. get. Karten. Liste() mein. Kartenhaufen. get. Wert() Zugriff nur mit Zugriffsmethoden Aufgabe: Welche Ergebnisse werden wohl an Stelle der Fragezeichen ausgegeben? Überprüfe deine Vermutung. Worin liegen die Vorteile, wenn nur die von einem Objekt bereitgestellten Methoden benutzt werden?
45 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.
46 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.
47 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.
48 Zugriffsrechte in Python class Kartenhaufen(object): def __init__(self): self. __karten. Liste = [] self. __wert = 0 def __kartenwert(self, karte): … def hinzufuegen(self, karte): self. __karten. Liste = self. __karten. Liste + [karte] self. __wert = self. __wert + self. __kartenwert(karte) def get. Karten. Liste(self): return self. __karten. Liste def get. Wert(self): return self. __wert 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. def set. Karten. Liste(self, vorgegebene. Karten): self. __karten. Liste = vorgegebene. Karten gesamtwert = 0 for karte in self. __karten. Liste: gesamtwert = gesamtwert + self. __kartenwert(karte) self. __wert = gesamtwert
49 Datenkapselung in Python >>> mein. Kartenhaufen = Kartenhaufen() >>> mein. Kartenhaufen. set. Karten. Liste(['K-A', 'H-A']) >>> mein. Kartenhaufen. __wert Traceback (most recent call last): File. . . mein. Kartenhaufen. __wert Attribute. Error: 'Kartenhaufen' object has no attribute '__wert' >>> mein. Kartenhaufen. __dict__ {'_Kartenhaufen__wert': 22, '_Kartenhaufen__karten. Liste': ['K-A', 'H-A']} >>> mein. Kartenhaufen. _Kartenhaufen__wert 22 Wie erwartet kann man auf das private Attribut __wert des Objekts mein. Kartenhaufen nicht zugreifen. Python meldet als Fehler, dass es kein Attribut __wert gibt. Der Aufruf mein. Kartenhaufen. __dict__ verrät, woran das liegt. Ein Aufruf wie mein. Kartenhaufen. __dict__ listet sämtliche Attribute mit den zugehörigen Attributwerten des betreffenden Objekts auf. Interessant ist hier, dass sich das private Attribut __wert hinter einem anderen Namen versteckt. Wenn man weiß, wie der neue Name - hier _Kartenhaufen__wert - 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.
50 Datenkapselung in Python >>> mein. Kartenhaufen = Kartenhaufen() >>> mein. Kartenhaufen. __karten. Liste Traceback (most recent call last): File … mein. Kartenhaufen. __karten. Liste Attribute. Error: 'Kartenhaufen' object has no attribute '__karten. Liste' >>> mein. Kartenhaufen. __dict__ {'_Kartenhaufen__wert': 0, '_Kartenhaufen__karten. Liste': []} >>> mein. Kartenhaufen. __karten. Liste = ['X-B'] >>> mein. Kartenhaufen. __karten. Liste ['X-B'] >>> mein. Kartenhaufen. __dict__ {'__karten. Liste': ['X-B'], '_Kartenhaufen__wert': 0, '_Kartenhaufen__karten. Liste': []} Ein erster Zugriff auf das private Attribut __karten. Liste scheitert. Dann aber ist es entgegen aller Zugriffslogik scheinbar möglich, dem privaten Attribut __karten. Liste einen Wert zuzuweisen. Der Aufruf mein. Kartenhaufen. __dict__ erklärt erst, was hier passiert ist. Neben dem privaten Attribut __karten. Liste, das sich hinter dem neuen Namen _Kartenhaufen__karten. Liste versteckt, gibt es noch ein öffentliches Attribut __karten. Liste, auf das man direkt zugreifen kann.
Datenkapselung in Python 51 class Kartenhaufen(object): __slots__ = ('__karten. Liste', '__wert') Mit dem Attribut __slots__ wird festgelegt, welche Attribute ein Objekt der betreffenden Klasse haben darf. def __init__(self): self. __karten. Liste = [] self. __wert = 0 #. . . wie bisher. . . >>> mein. Kartenhaufen = Kartenhaufen() >>> mein. Kartenhaufen. __karten. Liste Traceback (most recent call last): File. . . mein. Kartenhaufen. __karten. Liste Attribute. Error: 'Kartenhaufen' object has no attribute '__karten. Liste' >>> mein. Kartenhaufen. __karten. Liste = ['X-B'] Traceback (most recent call last): File. . . mein. Kartenhaufen. __karten. Liste = ['X-B'] Attribute. Error: 'Kartenhaufen' object has no attribute '__karten. Liste'
Vereinbarung zur Datenkapselung 52 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. class Kartenhaufen(object): __slots__ = ('karten. Liste', 'wert') def __init__(self): self. karten. Liste = [] self. wert = 0 #. . .
Übungen 53 class Kartenhand(object): def __init__(self): self. karten. Liste = [] self. anzahl = 0 def hinzufuegen(self, karte): self. karten. Liste = self. karten. Liste + [karte] self. anzahl = self. anzahl + 1 def wegnehmen(self, karte): if karte in self. karten. Liste: i = self. karten. Liste. index(karte) del self. karten. Liste[i] self. anzahl = self. anzahl - 1 Aufgabe 1: (a) Zeige mit einem geeigneten Python-Dialog, dass ein leichtfertiger Zugriff auf Attribute hier zu unerwünschten Objektzuständen führen kann. (b) Erstelle ein Klassendiagramm, in dem Zugriffsrechte festgelegt sind und benötigte Zugriffsmethoden eingeführt sind. Ergänze die Deklaration der Klasse Kartenhand entsprechend.
54 Übungen from random import randint class Kartenstapel(object): def __init__(self): self. karten. Liste = ['X-A', 'X-K', 'X-D', 'X-B', 'X-10', 'X-9', 'X-8', 'X-7', 'P-A', 'P-K', 'P-D', 'P-B', 'P-10', 'P-9', 'P-8', 'P-7', 'H-A', 'H-K', 'H-D', 'H-B', 'H-10', 'H-9', 'H-8', 'H-7', 'K-A', 'K-K', 'K-D', 'K-B', 'K-10', 'K-9', 'K-8', 'K-7'] def mischen(self): neue. Liste = [] aktuelle. Anzahl = len(self. karten. Liste) while aktuelle. Anzahl > 0: i = randint(0, aktuelle. Anzahl-1) neue. Liste = neue. Liste + [self. karten. Liste[i]] del self. karten. Liste[i] aktuelle. Anzahl = len(self. karten. Liste) self. karten. Liste = neue. Liste def ist. Leer(self): if len(self. karten. Liste) > 0: ergebnis = False else: ergebnis = True return ergebnis def karte. Ziehen(self): if len(self. karten. Liste) > 0: gezogene. Karte = self. karten. Liste[0] self. karten. Liste = self. karten. Liste[1: ] else: gezogene. Karte = None return gezogene. Karte Aufgabe 2: Gegeben ist eine Implementierung der Klasse Kartenstapel: Ergänze Zugriffsmethoden, um einen lesenden und einen schreibenden Zugriff auf das Attribut karten. Liste zu ermöglichen.
55 Teil 4 Modularisierung mit Klassen
56 Das Bausteinprinzip Wenn man die richtigen Bausteine zur Verfügung hat, dann ist es meist recht einfach, das gewünschte System zu entwickeln. Das gilt nicht nur für Systeme im Alltag, diese Aussage trifft auch auf die Entwicklung von Software zu. Wir haben in den vorangegangenen Abschnitten Programme zu Kartenspielen entwickelt. Dabei war es äußerst hilfreich, auf Klassen als fertige Softwarebausteine zurückgreifen zu können. Wir schauen uns hier noch diese Vorgehensweise genauer an. Insbesondere geht es um die Frage, wie eine Klasse aufbereitet werden sollte, damit sie von einem Benutzer direkt als Baustein verwendet werden kann.
57 Verwendung einer Klasse Ein Nutzer der Klasse Kartenstapel hat das folgende Testprogramm geschrieben. from kartenspiel import Kartenstapel # Testprogramm kartenstapel = Kartenstapel() kartenstapel. mischen() while not kartenstapel. ist. Leer(): kartenstapel. karte. Ziehen() karte = kartenstapel. get. Gezogene. Karte() print(karte) Schnittstelle zur Klasse Nutzung der Klasse Aufgabe: Über welche Informationen muss der Nutzer verfügen, um ein solches Testprogramm schreiben zu können?
58 Verwendung einer Klasse Der Entwickler der Klasse Kartenstapel veröffentlicht das folgende Klassendiagramm: Klassendiagramm Aufgabe: Welche Informationen über die Klasse Kartenstapel findet man hier? Welche zur Nutzung der Klasse benötigten Informationen sind hier nicht dokumentiert.
Verwendung einer Klasse 59 #-----------------------------# Kartenstapel #-----------------------------from random import randint class Kartenstapel(object): __slots__ = ('karten. Liste') def __init__(self): """ Die Methode __init__ erzeugt ein Kartenstapel-Objekt. Die 32 Karten werden hier in einer festen Reihenfolge in kodierter Form vorgegeben: 'X-A' (Kreuz Ass), . . . , 'K-7' (Karo 7) """ … self. karten. Liste = [ 'X-A', 'X-K', 'X-D', 'X-B', 'X-10', 'X-9', 'X-8', 'X-7', 'P-A', 'P-K', 'P-D', 'P-B', 'P-10', 'P-9', 'P-8', 'P-7', 'H-A', 'H-K', 'H-D', 'H-B', 'H-10', 'H-9', 'H-8', 'H-7', 'K-A', 'K-K', 'K-D', 'K-B', 'K-10', 'K-9', 'K-8', 'K-7' ] Python-Docstring Implementierung mit Schnittstellenbeschreibung
60 Verwendung einer Klasse >>> help(Kartenstapel) Help on class Kartenstapel in module __main__: class Kartenstapel(builtins. object) | Methods defined here: | | __init__(self) | Die Methode __init__ erzeugt ein Kartenstapel-Objekt. | Die 32 Karten werden hier in einer festen Reihenfolge | in kodierter Form vorgegeben: | 'X-A' (Kreuz Ass), . . . , 'K-7' (Karo 7) | | get. Karten. Liste(self) | Die noch auf den Kartenstapel befindlichen Karten werden | als Liste von Karten zurückgegeben. | | ist. Leer(self) | Die Methode liefert als Ergebnis True / False, | falls der Kartenstapel leer / nicht leer ist. | … | -----------------------------------| Data descriptors defined here: | | karten. Liste Zugriff auf die Schnittstellenbeschreibung
61 Fachkonzept - 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.
62 Fachkonzept - Schnittstelle 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. #-----------------------------# Kartenstapel - Schnittstellenbeschreibung #-----------------------------class Kartenstapel(object): __slots__ = ('karten. Liste') def __init__(self): """ Die Methode __init__ erzeugt ein Kartenstapel-Objekt. Die 32 Karten werden hier in einer festen Reihenfolge in kodierter Form vorgegeben: 'X-A' (Kreuz Ass), . . . , 'K-7' (Karo 7) """ …
63 Modulimport in Python import spielkarten # Testprogramm kartenstapel = spielkarten. Kartenstapel() kartenstapel. mischen() mein. Kartenhaufen = spielkarten. Kartenhaufen() while mein. Kartenhaufen. get. Wert() < 18: karte = kartenstapel. gib. Oberste. Karte() print(karte) mein. Kartenhaufen. hinzufuegen(karte) print(mein. Kartenhaufen. get. Wert()) # Baustein importieren from spielkarten import Kartenstapel, Kartenhaufen # Objekte erzeugen und verwenden kartenstapel = Kartenstapel() kartenstapel. mischen() mein. Kartenhaufen = Kartenhaufen() while mein. Kartenhaufen. get. Wert() < 18: karte = kartenstapel. gib. Oberste. Karte() print(karte) mein. Kartenhaufen. hinzufuegen(karte) print(mein. Kartenhaufen. get. Wert()) # Baustein importieren from spielkarten import * # Objekte erzeugen und verwenden. . . Die Namen „Kartenstapel„ und „Kartenhaufen“ werden in den aktuellen Namensraum übernommen.
Übungen 64 from random import randint class Lottogeraet(object): def __init__(self): # erzeugt e. Obj. z. Simulation e. Lottogeraets self. geraet. Vorbereiten() def geraet. Vorbereiten(self): # fuellt das Lottogeraet mit Kugeln von 1. . 49 # leert den Behaelter der gezogenen Kugeln self. vorhandene. Kugeln = list(range(1, 50)) self. gezogene. Kugeln = [] def kugel. Ziehen(self): … # zieht ei. Kugel aus den noch vorhand. Kugeln # entfernt diese Kugel aus dem Kugelbehaelter # legt d. Kugel i. Behaelter d. gezog. Kugeln ab Aufgabe 1: Zur Simulation von Lotto-Ziehungen wird hier eine Klasse Lottogeraet bereitgestellt. Du findest eine Implementierung dieser Klasse in der Datei lotto. py. (a) Teste die Klasse mit einem einfachen Testprogramm, das alle Methoden der Klasse benutzt. (b) Löse mit Hilfe der Klasse folgendes Problem: Wie oft kommt deine Lieblingszahl (z. B. die 13) in 1000 Lottoziehungen vor?
Übungen 65 class Kartenhand(object): def __init__(self, karten. Gegeben): self. karten. Liste = karten. Gegeben self. anzahl = len(self. karten. Liste) def existiert. Karte(self, karte): if karte in self. karten. Liste: ergebnis = True else: ergebnis = False return ergebnis def hinzufuegen(self, karte): if not self. existiert. Karte(karte): self. karten. Liste = self. karten. Liste + [karte] self. anzahl = self. anzahl + 1 def wegnehmen(self, karte): if self. existiert. Karte(karte): i = self. karten. Liste. index(karte) del self. karten. Liste[i] self. anzahl = self. anzahl - 1 Aufgabe 2: Betrachte die Klasse Kartenhand. (a) Ergänze zunächst geeignete Zugriffsmethoden für die Attribute der Klasse. (b) Ergänze die Implementierung um eine Schnittstellenbeschreibung, so dass die Klasse benutzt werden kann, ohne dass man sich die Implementierungsdetails anschauen muss.
Übungen 66 from random import randint Aufgabe 3: class Lottogeraet(object): def __init__(self): Betrachte noch einmal die Klasse Lottogeraet zur Simulation eines Lottogeräts (siehe lotto. py). # erzeugt e. Obj. z. Simulation e. Lottogeraets self. geraet. Vorbereiten() def geraet. Vorbereiten(self): # fuellt das Lottogeraet mit Kugeln von 1. . 49 # leert den Behaelter der gezogenen Kugeln self. vorhandene. Kugeln = list(range(1, 50)) self. gezogene. Kugeln = [] def kugel. Ziehen(self): … # zieht ei. Kugel aus den noch vorhand. Kugeln # entfernt diese Kugel aus dem Kugelbehaelter # legt d. Kugel i. Behaelter d. gezog. Kugeln ab Ziel ist es, die Implementierng der Klasse Lottogeraet so abzuändern, dass die vorhandenen bzw. gezogenen Kugeln mit Hilfe von Ankreuzfeldern (implementiert mit Listen mit 49 Wahrheitswerten) verwaltet werden. Am Verhalten der Methoden soll sich dabei aber nichts ändern. (a) Entwickle eine passende Implementierung. Benutze zum Testen das Testprogramm aus Aufgabe 1. (b) Was muss man nach Veränderungen in der Implementierung an der Schnittstellenbeschreibung ändern?
67 Teil 5 Vererbung
68 Kartenstapel als Stapel werden im Alltag benutzt, wenn man Gegenstände nach dem LIFO-Prinzip verwalten möchte. LIFO bedeutet dabei "last in, first out" bzw. "wer zuletzt kommt, wir als erstes bearbeit". Kartenstapel sind spezielle Stapel, bei denen Karten nach dem LIFO-Prinzip verwaltet werden. Kartenstapel weisen zusätzliche Besonderheiten auf. Die Karten eines Kartenstapels möchte man auch mischen können. Zielsetzung: Wir werden die Klasse Kartenstapel im Folgenden neu konzipieren. Wir werden dabei ausnutzen, dass Kartenstapel als spezielle Stapel aufgefasst werden können.
69 Stapel Zur Realisierung des LIFO-Prinzips benötigt man Operationen, um ein neues Element oben auf dem Stapel abzulegen und um das oberste Element vom Stapel zu entfernen.
70 Stapel Aufgabe: Implementiere die Klasse Stapel und teste sie sorgfältig. Stapel(p. Liste): erzeugt einen Stapel mit einer Ausgangsbelegung is. Empty(): überprüft, ob der Stapel Elemente enthält und liefert als Ergebnis einen Wahrheitswert zurück push(element): ein übergebenes Element wird oben auf den Stapel gelegt pop(): das oberste Element des Stapels wird entfernt und als Ergebnis zurückgeliefert (sofern der Stapel Elemente hat) top(): liefert das oberste Element des Stapels (falls ein solches existiert), entfernt es aber nicht aus dem Stapel …
71 Kartenstapel als spezieller Stapel Die Klasse Kartenstapel lässt sich jetzt leicht als Erweiterung der Klasse Stapel gewinnen. Die Idee ist hierbei, das Rad nicht noch einmal neu zu erfinden, sondern die bereits vorhandenen Funktionalitäten zu nutzen und gegebenenfalls zu erweitern oder auch abzuändern. Das folgende Klassendiagramm verdeutlicht diesen Erweiterungsprozess.
72 Kartenstapel als spezieller Stapel Die Klasse Stapel sieht bereits einige Methoden zur Realisierung eines Kartenstapels vor. So kann die Methode pop benutzt werden, um eine Karte vom Stapel zu ziehen. Mit der Methode is. Empty kann man überprüfen, ob noch Karten auf dem Stapel liegen. Noch nicht realisiert ist dagegen das Mischen der Karten. Ebenfalls noch nicht realisiert ist die adäquate Initialisierung des Stapel: Bei einem Kartenstapel sollten zunächst alle 32 Spielkarten im Stapel vorkommen.
Implementierung 73 from stapel import Stapel from random import randint class Kartenstapel(Stapel): def __init__(self): self. liste = [ 'X-A', 'X-K', 'X-D', 'X-B', 'X-10', 'X-9', 'X-8', 'X-7', 'P-A', 'P-K', 'P-D', 'P-B', 'P-10', 'P-9', 'P-8', 'P-7', 'H-A', 'H-K', 'H-D', 'H-B', 'H-10', 'H-9', 'H-8', 'H-7', 'K-A', 'K-K', 'K-D', 'K-B', 'K-10', 'K-9', 'K-8', 'K-7' ] def mischen(self): neue. Liste = [] aktuelle. Anzahl = len(self. liste) while aktuelle. Anzahl > 0: i = randint(0, aktuelle. Anzahl-1) neue. Liste = neue. Liste + [self. liste[i]] del self. liste[i] aktuelle. Anzahl = len(self. liste) self. liste = neue. Liste
74 Implementierung from kartenstapel import Kartenstapel # Test kartenstapel = Kartenstapel() kartenstapel. mischen() while not kartenstapel. is. Empty(): oberste. Karte = kartenstapel. pop() print(oberste. Karte)
75 Fachkonzept - 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.
76 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 selten nutzen.
- Slides: 76