konzepte der objektorientierten programmierung n.
Download
Skip this Video
Loading SlideShow in 5 Seconds..
Konzepte der objektorientierten Programmierung PowerPoint Presentation
Download Presentation
Konzepte der objektorientierten Programmierung

play fullscreen
1 / 114

Konzepte der objektorientierten Programmierung

115 Views Download Presentation
Download Presentation

Konzepte der objektorientierten Programmierung

- - - - - - - - - - - - - - - - - - - - - - - - - - - E N D - - - - - - - - - - - - - - - - - - - - - - - - - - -
Presentation Transcript

  1. Konzepte der objektorientierten Programmierung Klaus Becker 2013

  2. Simulation von Ampelsystemen "Objektorientierung ist die derzeitige Antwort auf die gestiegene Komplexität der Softwareentwicklung." Oestereich: Objektorientierte Software-Entwicklung Wir betrachten hier Ampelanlagen zur Regelung des Verkehrs an einer Straßenkreuzung - so, wie man es überall in der Welt vorfindet. Ziel ist es hier, solche Ampelanlagen mit Hilfe von Softwarebausteinen zu simulieren. Dabei sollen die Grundideen und Fachkonzepte der objektorientierten Programmierung Schritt für Schritt entwickelt werden.

  3. Teil 1 Objekte und Klassen

  4. Verwaltung von Ampeln Wir betrachten hier eine Straßenkreuzung mit 4 Ampeln. Ziel ist es hier, dieses recht einfache Ampelsysteme mit Hilfe von Softwarebausteinen zu simulieren. Bevor der Computer die Simulation übernehmen soll, spielen wir erst einmal die anstehende Datenverwaltung und Datenverarbeitung mit Personen durch.

  5. Rollenspiel zur Simulation Die Datenverwaltung und Datenverarbeitung wird für jede der 4 Ampeln von einer Person übernommen. Diese Person erhält hierzu einen Zettel, auf dem sie sich bestimmte Daten notieren kann. Zusätzlich gibt es eine Person, die die Arbeit der Ampeln jeweils aktiviert. Diese Person kann den "Ampel-Personen" spezielle, vorab festgelegte Anweisungen erteilen. An ampel1: … … ampel1

  6. Rollenspiel zur Simulation Aufgabe: Damit das Rollenspiel funktioniert, müssen zuerst Vereinbarungen getroffen werden. Überlegt euch vorab, was die "Ampel-Personen" auf dem Zettel notieren sollen und welche Anweisungen die "Ampel-Personen" ausführen können soll. Beachtet, dass die Ampeln zu Beginn in einen Ausgangszustand gebracht werden müssen. An ampel1: … … ampel1

  7. Ein Ampel-Objekt zur Simulation Welche Daten muss ein Software-Objekt zur Simulation einer Ampel verwalten? Ein solches Software-Objekt Ampel muss sich in irgendeiner Form "merken", in welcher Phase sich die simulierte Ampel aktuell befindet. Hierzu kann dieses Software-Objekt z.B. die Zustände der drei Lampen registrieren. Welche Operationen muss ein Software-Objekt zur Simulation einer Ampel ausführen können? Zum einen muss es eine Operation geben, mit der man ein Software-Objekt in einen Ampel-Anfangszustand versetzen kann. Zum anderen muss es eine Operation geben, mit der man das Weiterschalten der Ampel bewirken kann.

  8. Ein Bauplan für Ampel-Objekte class Ampel(object): def __init__(self): self.lampeRot = False self.lampeGelb = False self.lampeGruen = False def setLampen(self, startwertLampeRot, startwertLampeGelb, startwertLampeGruen): self.lampeRot = startwertLampeRot self.lampeGelb = startwertLampeGelb self.lampeGruen = startwertLampeGruen def schalten(self): if (self.lampeRot, self.lampeGelb, self.lampeGruen) == (True, False, False): self.lampeGelb = True # elif ... Aufgabe: Ergänze zunächst die Klassendeklaration und speichere sie in einer Datei mit geeignetem Namen (z. B. ampel.py) ab.

  9. Ampel-Objekt in Aktion >>> >>> ampel1 = Ampel() >>> ampel1.lampeRot False >>> ampel1.lampeGelb False >>> ampel1.lampeGruen False >>> ampel1.setLampen(True, False, False) >>> ampel1.lampeRot True >>> ampel1.lampeGelb False >>> ampel1.lampeGruen False >>> ampel1.schalten() >>> ampel1.lampeRot True >>> ampel1.lampeGelb True >>> ampel1.lampeGruen False Aufgabe (a) Welche Anweisung erzeugt hier wohl das Objekt ampel1? Wie inspiziert man den Zustand der verwalteten Lampen? Wie aktiviert man das Objekt, so dass es eine der in der Klasse festgelegten Operationen ausführt? (b) Führe den gezeigten Python-Dialog aus und setze ihn so fort, dass ein gesamter Ampelzyklus durchlaufen wird. Kontrolliere so, ob die vervollständigte Klassendeklaration eine Ampel korrekt beschreibt.

  10. Ampel-Objekt in Aktion class Ampel(object): def __init__(self): ... def setLampen(self, startwertLampeRot, startwertLampeGelb, startwertLampeGruen): ... def schalten(self): ... # Test ampel1 = Ampel() ampel1.setLampen(True, False, False) print(ampel1.lampeRot, ampel1.lampeGelb, ampel1.lampeGruen) Aufgabe Das Verhalten von Objekten kann man auch mit einem Programm testen. (a) Führe das gezeigte Testprogramm aus und erkläre sein Verhalten. (b) Das zu simulierende Ampelsystem (siehe oben) besteht aus 4 Ampeln. Ändere das Testprogramm so ab, dass ein kompletter Ampelzyklus des Ampelsystems simuliert wird.

  11. Objekte unserer Welt Objekte unserer Welt sind fassbare Gegenstände (wie z.B. Schuhe oder Kartenstapel) oder auch Konstrukte unseres Denkens und Handelns (wie z.B. Schuhladen oder Kartenspiel). Betrachten wir das Beispiel "Schuhe". Wenn man seine neuen Schuhe charakterisieren will, dann fallen einem sofort eine Reihe von Eigenschaften ein: Modell: Sneaker; Größe: 40; Farbe: rot; Verschluss: Schnürsenkel usw.. Schuhe haben nicht nur bestimmte Eigenschaften, mit ihnen kann man auch bestimmte "Operationen" ausführen: Zum An- und Ausziehen kann man sie in einem gewissen Sinn öffnen und schließen (z.B. durch Öffnen und Binden der Schnürsenkeln). Vielleicht kann man sie auch benutzen, um einen Nagel einzuschlagen, obwohl sie dafür eigentlich nicht gedacht sind. Objekte prägen sehr stark unser Denken. Wir können die Welt, in der wir leben, mit Hilfe von Objekten beschreiben, die Eigenschaften haben und die mit bestimmten Operationen bearbeitet werden können.

  12. Software-Objekte Analog zu einer Ampel (als Objekt der realen Welt) soll ein Software-Objekt ampel konzipiert werden. Das folgende Ablaufprotokoll verdeutlicht die Arbeitsweise eines Software-Objekts ampel: Das Software-Objekt verwaltet Daten zu bestimmten Eigenschaften einer Ampel (im vorliegenden Fall ist das die Eigenschaft, ob die jeweilige Lampe an oder aus ist). Das Software-Objekt ampel nutzt hierzu die Variablen lampeRot, lampeGelb und lampeGruen. Das Software-Objekt ampel stellt auch Operationen zur Verarbeitung der verwalteten Daten bereit. Im vorliegenden Fall gibt es z.B. Operation „weiterschalten", die durch die Prozedur schalten() dargestellt wird. Das Software-Objekt ist also eine Einheit, die Daten verwaltet und Operationen zur Verarbeitung der verwalteten Daten zur Verfügung stellt. Statt von Software-Objekten wird im Folgenden kurz von Objekten gesprochen.

  13. Fachkonzept - Objekt Ein Objekt ist eine Einheit, die Daten mit Hilfe von Attributen verwalten und Operationen zur Verarbeitung der verwalteten Daten mit Hilfe von Methoden ausführen kann. 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

  14. Fachkonzept - Objekt Zugriff auf Attribute: Objekt objekt.attribut ampel1.lampeGruen Aktivierung von Methoden: objekt.methode Attribute - Attributwerte ampel1.schalten() Ausführung einer Methode Objektdiagramm

  15. Klasse als Bauplan Der Begriff "Klasse" wird hier im Sinne von Klassifizieren benutzt. Du weißt sicher, was das heißt: Wenn man klassifiziert, dann versucht man, Gemeinsamkeiten von Objekten herauszustellen. Schuhe Schuhe Schuhe Die Klasse "Schuh" beschreibt Objekte, die man als Fußbekleidung nutzt - und somit an- und ausziehen sowie tragen kann - und die bestimmte Eigenschaften (wie Modell, Größe, Farbe und Verschluss) aufweisen. Klassendiagramm Wer Schuhe herstellen will, muss sich (mehr oder weniger) an der Klassenbeschreibung für Schuhe orientieren, damit das, was hergestellt wird, auch wirklich Schuhe sind. Es macht sicher keinen Sinn, sich an der Klassenbeschreibung für Hosen oder Pullover zu orientieren. Eine Klassenbeschreibung für Schuhe kann somit als eine Art Bauplan für Schuhe aufgefasst werden.

  16. Fachkonzept - Klasse Eine Klasse ist ein Bauplan für Objekte. Dieser Bauplan legt genau fest, welche Attribute die zu konstruierenden Objekte haben sollen und welche Methoden sie ausführen können sollen. Klassendiagramm Ein Objekt (als Exemplar einer Klasse) ist eine Einheit, die nach dem Bauplan der zugeordneten Klasse erzeugt wurde. Ein Objekt verfügt somit über die Attribute, die in der Klasse festgelegt sind. Diesen Attributen können - im Unterschied zur Klasse - Attributwerte zugewiesen werden. Ein Objekt kann zudem sämtliche Methoden der Klasse ausführen. Ausgenommen bleibt hier nur die Methode, deren Name mit dem Klassennamen übereinstimmt (s. u.). Objekte können mit Namen versehen werden, über die sie dann gezielt angesprochen werden können.

  17. Konstruktor / Destruktor Zur Erzeugung von Objekten verfügt eine Klasse über eine spezielle Methode, die sogenannte Konstruktormethode. Zur Vernichtung von Objekten verfügt eine Klasse über eine sogenannte Destruktormethode. Konstruktor Ein Software-Objekt hat - wie Objekte der realen Welt - eine bestimmte Lebensdauer. Es muss erzeugt werden, bevor es in Aktion treten kann, und kann auch wieder vernichtet werden. In einem Klassendiagramm wird eine Konstruktormethode dadurch gekennzeichnet, dass sie denselben Namen wie die Klasse selbst trägt. Oft wird diese spezielle Methode in Klassendiagrammen aber auch weggelassen. Beachte, dass eine Konstruktormethoden keine Methode ist, die ein Objekt ausführen kann. Destruktormethoden werden in der Regel in Klassendiagrammen weggelassen.

  18. Klassendeklaration in Python Klassenname Oberklasse Schlüsselwort Doppelpunkt class Ampel(object): def __init__(self): self.lampeRot = False self.lampeGelb = False self.lampeGruen = False def setLampen(self, startwertLampeRot, …): self.lampeRot = startwertLampeRot self.lampeGelb = startwertLampeGelb self.lampeGruen = startwertLampeGruen def schalten(self): … Einrückung Konstruktor Attribute Attribute Methode

  19. Objekterzeugung in Python Erzeugung eines Objekts >>> ampel1 = Ampel() >>> ampel1 <__main__.Ampel object at 0x0136C4B0> >>> ampel1.__dict__ {'lampeRot': False, 'lampeGelb': False, 'lampeGruen': False} Inspektion eines Objekts >>> ampel1 = Ampel() >>> ampel1 <__main__.Ampel object at 0x0136C4B0> >>> del ampel1 >>> ampel1 Traceback (most recent call last): File ... ampel1 NameError: name 'ampel1' is not defined >>> Vernichtung eines Objekts

  20. Übungen Aufgabe 1: Das Verhalten einer Ampel lässt sich mit dem folgenden Zustandsdiagramm abstrahierend beschreiben. Die Ampel befindet sich immer in einem der Zustände "rot", "rotgelb", "gruen" oder "gelb". Die Markierung am Zustand "rot" soll bedeuten, dass sich eine Ampel zu Beginn in diesem Zustand befindet. Mit der Aktion "schalten" wird jeweils ein Zustandsübergang ausgelöst. class Ampel(object): def __init__(self): self.zustand = 'rot' def setZustand(self, anfangszustand): self.zustand = anfangszustand def schalten(self): if self.zustand == 'rot': self.zustand = 'rotgelb' # elif ... def getLampen(self): if self.zustand == 'rot': lampen = (True, False, False) # elif ... return lampen (a) Ergänze die Klassendeklaration. Teste sie anschließend, indem du ein Objekt der Klasse Ampel erzeugst und geeignet aktivierst. (b) Zeichne ein Klassendiagramm, das zu dieser Implementierung passt.

  21. Übungen Aufgabe 1: (c) Wir ändern den Konstruktor der Klasse Ampel wie folgt ab. Stelle Vermutungen auf, was im folgenden Python-Dialog an Stelle der Fragezeichen steht. Überprüfe deine Vermutung. class Ampel(object): def __init__(self, anfangszustand): self.zustand = anfangszustand def setZustand(self, anfangszustand): self.zustand = anfangszustand def schalten(self): if self.zustand == 'rot': self.zustand = 'rotgelb' # elif ... def getLampen(self): if self.zustand == 'rot': lampen = (True, False, False) # elif ... return lampen >>> a1 = Ampel('rot') >>> a2 = Ampel('gruen') >>> a1.getLampen() ? >>> a2.getLampen() ? >>> a1.schalten() >>> a2.schalten() >>> a1.setZustand('rot') >>> a1.getLampen() ? >>> a2.getLampen() ?

  22. Übungen Aufgabe 2: Entwickle und teste eine Klasse zur Simulation von Fußgängerampeln. Aufgabe 3: Viele Ampeln verhalten sich nachts anders als tagsüber. Nachts wird nur die gelbe Lampe aus- und eingeschaltet. Dieses Verhalten soll mit einer weiterentwickelten Klasse Ampel simuliert werden. Das Klassendiagramm ist wie folgt gegeben: (a) Mache dir zunächst klar, wie die Tag-Nacht-Ampel funktionieren soll. (b) Entwickle eine passende Implementierung und teste sie.

  23. Teil 2 Modularisierung

  24. Das Bausteinprinzip Modularisierung ist ein Prinzip, nach dem viele Systeme entwickelt werden. Die Idee besteht darin, das Gesamtsystem nach dem Baukastenprinzip aus Einzelbausteinen (den sogenannten Modulen) zusammenzusetzen. "Unsere Partyzelte können in verschiedenen Größen aufgebaut werden. Da die Partyzelte und Festzelte aus Modulen bestehen, ist es sehr einfach, sie zu erweitern. Die Abbildung zeigt ein mögliches Kombinationsbeispiel der Module." Ein Softwaremodul ist eine in sich abgeschlossene Programmeinheit, die man vielfältig bei Problemlösungen einsetzen kann. Grundidee der objektorientierten Modularisierung ist es, solche Softwaremodule als Klassen zu konzipieren. Wir werden uns in diesem Abschnitt intensiver mit Problemen auseinander setzen, die bei der Verwendung von Klassen als Softwarebausteine entstehen.

  25. Experimente mit Ampel-Objekten … def aktualisiereLampen(self): if self.zustand == 'rot': self.lampeRot = True self.lampeGelb = False self.lampeGruen = False elif self.zustand == 'rotgelb': self.lampeRot = True self.lampeGelb = True self.lampeGruen = False elif self.zustand == 'gruen': self.lampeRot = False self.lampeGelb = False self.lampeGruen = True elif self.zustand == 'gelb': self.lampeRot = False self.lampeGelb = True self.lampeGruen = False class Ampel(object): def __init__(self): self.zustand = 'rot' self.lampeRot = None self.lampeGelb = None self.lampeGruen = None self.aktualisiereLampen() def getLampen(self): return (self.lampeRot, self.lampeGelb, self.lampeGruen) def schalten(self): if self.zustand == 'rot': self.zustand = 'rotgelb' elif self.zustand == 'rotgelb': self.zustand = 'gruen' elif self.zustand == 'gruen': self.zustand = 'gelb' elif self.zustand == 'gelb': self.zustand = 'rot' self.aktualisiereLampen() … Es gibt eine Vielzahl von Möglichkeiten, eine Klasse Ampel zur Simulation einer solchen Ampel zu konzipieren.

  26. Experimente mit Ampel-Objekten >>> a = Ampel() >>> a.zustand 'rot' >>> a.getLampen() (True, False, False) >>> a.schalten() >>> a.zustand 'rotgelb' >>> a.getLampen() (True, True, False) >>> a.zustand = 'gruen' >>> a.zustand 'gruen' >>> a.getLampen() (True, True, False) class Ampel(object): def __init__(self): self.zustand = 'rot' self.lampeRot = None self.lampeGelb = None self.lampeGruen = None self.aktualisiereLampen() def getLampen(self): return (self.lampeRot, self.lampeGelb, self.lampeGruen) def schalten(self): if self.zustand == 'rot': self.zustand = 'rotgelb' elif self.zustand == 'rotgelb': self.zustand = 'gruen' elif self.zustand == 'gruen': self.zustand = 'gelb' elif self.zustand == 'gelb': self.zustand = 'rot' self.aktualisiereLampen() … Aufgabe: Führe selbst diesen Dialog aus. Warum ist dieser Dialog nicht im Sinne des Ampelsystems? Woran liegt das?

  27. Experimente mit Ampel-Objekten >>> a = Ampel() >>> a.getZustand() 'rot' >>> a.getLampen() (True, False, False) >>> a.schalten() >>> a.getZustand() 'rotgelb' >>> a.getLampen() (True, True, False) >>> a.setZustand('gruen') >>> a.getZustand() 'gruen' >>> a.getLampen() (False, False, True) class Ampel(object): def __init__(self): self.zustand = 'rot' self.lampeRot = None self.lampeGelb = None self.lampeGruen = None self.aktualisiereLampen() def setZustand(self, z): self.zustand = z self.aktualisiereLampen() def getZustand(self): return self.zustand def getLampen(self): return (self.lampeRot, self.lampeGelb, self.lampeGruen) def schalten(self): if self.zustand == 'rot': self.zustand = 'rotgelb' … Kein Zugriff auf die Attribute

  28. Experimente mit Ampel-Objekten >>> a = Ampel() >>> a.getZustand() 'rot' >>> a.getLampen() (True, False, False) >>> a.schalten() >>> a.getZustand() 'rotgelb' >>> a.getLampen() (True, True, False) >>> a.setZustand('gruen') >>> a.getZustand() 'gruen' >>> a.getLampen() (False, False, True) class Ampel(object): def __init__(self): self.zustand = 'rot' self.lampeRot = None self.lampeGelb = None self.lampeGruen = None self.aktualisiereLampen() def setZustand(self, z): self.zustand = z self.aktualisiereLampen() def getZustand(self): return self.zustand def getLampen(self): return (self.lampeRot, self.lampeGelb, self.lampeGruen) def schalten(self): if self.zustand == 'rot': self.zustand = 'rotgelb' … Klasse mit Zugriffsmethoden Aufgabe: Warum ist es sinnvoll, dass Benutzer einer Klasse nur die Methoden der Klasse verwenden?

  29. Das Geheimnisprinzip Wenn man die Motorhaube eines neueren Autos öffnet, dann sieht man recht wenig vom Motor. Einblick in das eigentliche Geschehen im Motor hat man nicht, wesentliche Teile des Motors werden sogar durch Abdeckungen schwer zugänglich gemacht. Man kann allenfalls überprüfen, ob man genug Öl oder Bremsflüssigkeit hat. Diese Vorgehensweise, den Motor eines Autos nur noch für Spezialisten zugänglich zu machen, wird ganz bewusst von den Autobauern gewählt. Ein Motor ist heutzutage so kompliziert, dass Laien keine Veränderungen daran vornehmen sollen. Beim Autobau wird somit - zumindest in bestimmten Bereichen - das Geheimnisprinzip angewandt. Bestimmte Eigenschaften des Motors können nur über speziell hierfür vorgesehene Schnittstellen ermittelt werden. So kann der aktuelle Ölstand nur an einem hierfür vorgesehenen Messstab abgelesen werden. Änderungen am aktuellen Motorzustand können direkt ebenfalls nur an bestimmten hierfür vorgesehenen Stellen vorgenommen werden. Motoröl lässt sich nur in die hierfür vorgesehene Öffnung einfüllen. Alles weitere über das Innere des Motors bleibt für den normalen Autofahrer unzugänglich und in diesem Sinne geheim.

  30. Fachkonzept - Datenkapselung Software-Objekte (als Programmeinheiten) werden so konzipiert, dass Details über den inneren Aufbau verborgen werden und Änderungen von Objektzuständen nur über dafür vorgesehene Methoden erfolgen können. Das Verbergen des inneren Aufbaus wird realisiert, indem man keinen direkten Zugriff auf die Attribute zur Verwaltung der internen Daten eines Objekts ermöglicht. Man nennt diese Vorgehensweise auch Datenkapselung.

  31. Zugriffsrechte / Zugriffsmethoden Um interne Daten kapseln zu können, werden Zugriffrechte festgelegt. Der Entwickler einer Klasse hat die Möglichkeit, Attribute und Methoden einer Klasse als öffentlich oder privat zu deklarieren. Lesende und schreibende Zugriffe auf Attribute bzw. Methoden eines Objekts sind nur möglich, wenn diese öffentlich sind. Private Attribute bzw. Methoden können dagegen nur bei der Implementierung der betreffenden Klasse benutzt werden. Im Klassendiagramm werden die Zugriffsrechte auf die Attribute und Methoden mit Hilfe der Symbole + (für öffentlich) und - (für privat) festgelegt. Verfolgt man die Strategie, alle Attribute als privat zu deklarieren, so besteht keine Möglichkeit, direkt schreibend oder lesend auf Attributwerte zuzugreifen. Um dennoch solche Zugriffe zu erlauben, werden spezielle öffentliche Zugriffsmethoden bereitgestellt. Das Klassendiagramm wird daher um solche Zugriffsmethoden erweitert.

  32. Zugriffsrechte in Python class Ampel(object): def __init__(self): self.__zustand = 'rot' def schalten(self): if self.__zustand == 'rot': self.__zustand = 'rotgelb' elif self.__zustand == 'rotgelb': self.__zustand = 'gruen' … def getLampen(self): if self.__zustand == 'rot': lampen = (True, False, False) … return lampen def getZustand(self): return self.__zustand def setZustand(self, z): self.__zustand = z Ein Attribut wird in Python zu einem privaten Attribut, wenn der Name mit zwei Unterstrichen beginnt und nicht mit Unterstrichen endet. Beginnt der Attributname / Methodenname nicht mit einem Unterstrich, so ist das Attribut öffentlich. Entsprechendes gilt für Methoden.

  33. Datenkapselung in Python >>> a = Ampel() >>> a.__zustand Traceback (most recent call last): File ... a.__zustand AttributeError: 'Ampel' object has no attribute '__zustand' >>> a.__dict__ {'_Ampel__zustand': 'rot'} >>> a._Ampel__zustand 'rot' Wie erwartet kann man auf das private Attribut __zustand des neu erzeugten Objekts a nicht zugreifen. Python meldet als Fehler, dass es kein Attribut __zustand gibt. Der Aufruf a.__dict__ verrät, woran das liegt. Ein Aufruf wie a.__dict__ listet sämtliche Attribute mit den zugehörigen Attributwerten des betreffenden Objekts auf. Interessant ist hier, dass sich das private Attribut __zustand hinter einem anderen Namen versteckt. Wenn man weiß, wie der neue Name - hier _Ampel__zustand - gebildet wird, dann kann man auf das betreffende Attribut zugreifen. Also: Private Attribute werden in Python mit anderen Namen versehen, so dass kein direkter Zugriff möglich ist. Kennt man den Namen, hinter dem sich ein privates Attribut verbirgt, so kann man durchaus auf dieses Attribut zugreifen. Python liefert also keinen echten Zugriffsschutz.

  34. Datenkapselung in Python >>> a = Ampel() >>> a.__zustand Traceback (most recent call last): File ... a.__zustand AttributeError: 'Ampel' object has no attribute '__zustand' >>> a.__dict__ {'_Ampel__zustand': 'rot'} >>> a._Ampel__zustand 'rot' >>> a.__zustand = 'gruen' >>> a.__zustand 'gruen' >>> a.__dict__ {'_Ampel__zustand': 'rot', '__zustand': 'gruen'} Ein erster Zugriff auf das private Attribut __zustand scheitert. Dann aber ist es - entgegen aller Zugriffslogik - scheinbar möglich, dem privaten Attribut __zustand einen Wert zuzuweisen. Der Aufruf a.__dict__ erklärt erst, was hier passiert ist. Neben dem privaten Attribut __zustand, das sich hinter dem neuen Namen _Ampel__zustand versteckt, gibt es noch öffentliches Attribut __zustand, auf das man direkt zugreifen kann.

  35. Datenkapselung in Python class Ampel(object): __slots__ = ('__zustand') def __init__(self): self.__zustand = 'rot' # ... wie bisher ... Mit dem Attribut __slots__ wird festgelegt, welche Attribute ein Objekt der betreffenden Klasse haben darf. >>> a.__zustand Traceback (most recent call last): File ... a.__zustand AttributeError: 'Ampel' object has no attribute '__zustand' >>> a.__zustand = 'gruen' Traceback (most recent call last): File ... a.__zustand = 'gruen' AttributeError: 'Ampel' object has no attribute '__zustand'

  36. Datenkapselung in Python Wir werden im Folgenden bei der Implementierung von Klassen in Python keine Attribute und Methoden als privat deklarieren. Alle Attribute und Methoden sind daher direkt zugänglich. Allerdings werden wir von dem direkten Zugriff in der Regel keinen Gebrauch machen. Nur in begründeten Sonderfällen (wie z.B. zum schnellen Testen) werden wir von dieser Vereinbarung abweichen.

  37. Verwendung einer Klasse Wir werden uns hier mit folgender Frage beschäftigen: Welche Information benötigt man über eine Klasse, um sie als Baustein zur Erzeugung und Aktivierung von Software-Objekten benutzen zu können? from ampel import Ampel a1 = Ampel('rot') a2 = Ampel('gruen') print('Ampel 1:', a1.getLampen()) print('Ampel 2:', a2.getLampen()) print() while a1.getZustand() != 'gelb': a1.schalten() a2.schalten() print('Ampel 1:', a1.getLampen()) print('Ampel 2:', a2.getLampen()) print() Nutzung einer Klasse Aufgabe: Was muss der Nutzer alles über die Klasse Ampel wissen, um ein solches Testprogramm schreiben zu können?

  38. Verwendung einer Klasse Der Entwickler der Klasse Kartenstapel veröffentlicht das folgende Klassendiagramm: Klassendiagramm Aufgabe: Welche Informationen über die Klasse Ampel findet man hier? Welche zur Nutzung der Klasse benötigten Informationen sind hier nicht dokumentiert.

  39. Verwendung einer Klasse class Ampel(object): def __init__(self, anfangszustand): # nachher: Ein Objekt der Klasse Ampel ist erzeugt. Der Wert von zustand ist gesetzt. self.zustand = anfangszustand def setZustand(self, z): # vorher: # Der Wert des Attributs zustand beschreibt eine Ampelphase. # nachher: # Dem Attribut zustand ist der Wert des übergebenen Parameters z zugewiesen. def getZustand(self): # Die Funktion ändert den Objektzustand nicht. # Die Funktion liefert als Ergebnis den Wert von zustand zurück. def schalten(self): # vorher: # Der Wert des Attributs zustand beschreibt eine Ampelphase. # nachher: # Der Wert des Attributs zustand beschreibt die nächste Phase gemäß # des üblichen Ampelzyklus "rot -> rotgelb > gruen -> gelb -> rot". def getLampen(self): # Die Funktion ändert den Objektzustand nicht. # Die Funktion liefert als Ergebnis ein Tripel aus Wahrheitswerten, # die den zur Phase passenden Lampenzustand in der Reihenfolge # (Lampe-rot, Lampe-gelb, Lampe-grün) beschreibt. Schnittstellenbeschreibung

  40. 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. class Ampel(object): def __init__(self): # nachher: # Ein Objekt der Klasse Ampel ist erzeugt. # Der Wert des Attributs zustand wird auf den übergebenen # Parameter gesetzt. def setZustand(self, z): # vorher: # Der Wert des Attributs zustand beschreibt eine Ampelphase. # nachher: # Dem Attribut zustand ist der Wert des übergebenen Parameters # z zugewiesen. …

  41. Modulimport in Python # Baustein importieren from ampel import Ampel # Objekt erzeugen a = Ampel('rot') # Objekt in Aktion print(a.getLampen()) a.schalten() print(a.getLampen()) while a.getZustand() != 'rot': a.schalten() print(a.getLampen()) # Baustein importieren import ampel # Objekt erzeugen a = ampel.Ampel('rot') # Objekt in Aktion print(a.getLampen()) a.schalten() print(a.getLampen()) while a.getZustand() != 'rot': a.schalten() print(a.getLampen()) # Baustein importieren from ampel import * # Objekt erzeugen a = Ampel('rot') # Objekt in Aktion ... Der Name „Ampel“ wird in den aktuellen Namensraum übernommen.

  42. Übungen class Ampel(object): def __init__(self, anfangszustand): self.lampeRot = # ... self.lampeGelb = # ... self.lampeGruen = # ... def schalten(self): # ... def getLampen(self): return # ... def getZustand(self): return # ... def setZustand(self, z): # ... Aufgabe 1: Es kommt des öfteren vor, dass eine Implementierung einer Klasse ausgetauscht werden soll. Wir spielen das im Folgenden einmal durch. (a) Ergänze die folgende Implementierung der Klasse Ampel so, dass die Methoden genau dasselbe Verhalten zeigen wie in der Schnittstellenbeschreibung zur Klasse (siehe Folie 39). (b) Warum sollte man keine Zugriffe auf Attribute benutzen? Begründe mit dem Austausch einer Implementierung.

  43. Anwendung – Ampel mit GUI Ziel ist es, eine einfache grafische Benutzeroberfläche zu erzeugen, von der aus ein Objekt der Klasse Ampel aktiviert werden kann. Die GUI soll dabei ganz einfach gestaltet sein. Wir benutzen eine dokumentierte Implementierung der Klasse Ampel, die sich in der Datei ampel.py befindet. Der Quelltext zeigt eine einfache Möglichkeit, ein Ampel-Objekt mit einer GUI zu verknüpfen. Wir werden diese Verknüpfungsproblematik in einem der folgenden Abschnitte noch einmal aufgreifen und allgemeiner lösen.

  44. Anwendung – Ampel mit GUI #--------------------------------------------------------------- # Datenmodell #--------------------------------------------------------------- from ampel import Ampel ampel = Ampel('rot') #--------------------------------------------------------------- # GUI #--------------------------------------------------------------- def anzeigeAktualisieren(lampeRot, lampeGelb, lampeGruen): … def buttonWeiterClick(): # Verarbeitung der Daten # ... Ampel weiter schalten # Aktualisierung der Anzeige # ... Lampenzustand abfragen und anzeigen from tkinter import * # Erzeugung des Fensters … Aufgabe 1: (a) Mache dich mit dem Quelltext vertraut (siehe inf-schule). Ergänze die Ereignisverarbeitungsprozedur und teste das Programm. (b) Ergänze das Programm so, dass zwei Ampeln simuliert werden.

  45. Teil 3 Beziehungen zwischen Objekten

  46. Steuerung mehrerer Ampeln Ziel ist es, komplexere Ampelsysteme, die aus mehreren Ampeln bestehen, mit Hilfe von Softwareobjekten zu simulieren. Dabei soll das Zusammenspiel der Objekte im Vordergrund stehen.

  47. Ampeln kopieren Wir betrachten ein System aus zwei Ampeln, die im Gegentakt schalten sollen. >>> a1 = Ampel(‘rot') >>> a2 = a1 >>> a2.setZustand('gruen') >>> a1.getZustand() ? >>> a2.getZustand() ? >>> a1.schalten() >>> a2.schalten() >>> a1.getZustand() ? >>> a2.getZustand() ? Aufgabe: (a) Stelle Vermutungen auf, was anstelle der Fragezeichen jeweils steht. Teste, ob deine Vermutungen stimmen. (b) Kannst du die Ergebnisse erklären? (c) Wie muss man vorgehen, wenn man unabhängig voneinander arbeitende Ampeln simulieren möchte?

  48. Identität von Objekten (Daten-) Objekte haben - analog zu Objekten unserer Lebenswelt - ebenfalls eine Identität. Zur eindeutigen Identifizierung werden sie mit Identitätsnummern versehen. Verschiedene Objekte unterscheiden sich in ihrer Identitätsnummer. Sie können aber durchaus denselben Objektzustand haben. Ein Objekt behält während seiner Lebensdauer immer die einmal vergebene Identitätsnummer. Auch wenn sich der Zustand des Objekts verändert, so bleibt doch die Identitätsnummer des Objekts bestehen. Häufig verwendet hierzu man eine Adresse im Speicher des Rechners als Identitätsnummer. Die Identitätsnummer eines Objekts zeigt dann auf den Speicherbereich, in dem die Daten des Objekts abgelegt sind. Diese Identifikation von Objekten durch eine Lokalisierung im Speicher setzt natürlich voraus, dass Objekte im Speicher nicht hin und her wandern, sondern dass der einmal zugeteilte Speicherbereich während der Lebensdauer eines Objekts bestehen bleibt. Wir gehen im Folgenden von dieser Vorstellung aus. >>> a1 = Ampel(‘rot') >>> a1 <__main__.Ampel object at 0x013….> >>> id(a1) 20311472 >>> hex(20311472) '0x135edb0' >>> a1 = Ampel(‘rot') >>> id(a1) 20311472 >>> a2 = a1 >>> id(a2) 20311472 >>> a1 = Ampel(‘rot') >>> id(a1) 20311472 >>> a2 = Ampel(‘rot') >>> id(a2) 20312048

  49. Zeiger / Referenzen Eine Variable ist ein Name, der (in der Regel) mit einem Objekt verknüpft ist. Wenn eine Variable ein (Daten-) Objekt verwaltet, dann verwaltet es die Speicheradresse (bzw. Identitäsnummer) dieses Objekts. Da die Speicheradresse auf das Objekt zeigt bzw. das Objekt referenziert, nennt man eine solche Adresse auch Zeiger bzw. Referenz und die Variable zur Verwaltung der Adresse Zeigervariable bzw. Referenzvariable.

  50. Zuweisungen bei Zeigervariablen >>> a1 = Ampel(‘rot') >>> id(a1) 20311472 >>> a2 = Ampel(‘rot') >>> id(a2) 20312048 a1 = Ampel(‘rot') a2 = a1 >>> a1 = Ampel(‘rot') >>> id(a1) 20311472 >>> a2 = a1 >>> id(a2) 20311472 a1.schalten() a2 = Ampel(‘rot')