Befrager-Architektur / Beobachter-Architektur
Das Datenmodell befragen
Wir betrachten zunächst noch einmal ein Programm zur Simulation der Ampel.
class Ampel(object):
def __init__(self, anfangszustand):
self.zustand = anfangszustand
def setZustand(self, z):
self.zustand = z
def getZustand(self):
return self.zustand
def schalten(self):
if self.zustand == 'rot':
self.zustand = 'rotgelb'
elif self.zustand == 'rotgelb':
self.zustand = 'gruen'
elif self.zustand == 'gruen':
self.zustand = 'gelb'
elif self.zustand == 'gelb':
self.zustand = 'rot'
def getLampen(self):
if self.zustand == 'rot':
lampen = (True, False, False)
elif self.zustand == 'rotgelb':
lampen = (True, True, False)
elif self.zustand == 'gruen':
lampen = (False, False, True)
elif self.zustand == 'gelb':
lampen = (False, True, False)
return lampen
from tkinter import *
grau = '#404040'
rotAn = '#FF0000'
rotAus = '#550000'
gelbAn = '#FFFF00'
gelbAus = '#555500'
gruenAn = '#00FF00'
gruenAus = '#005500'
class GUI(object):
def __init__(self, datenmodell):
self.ampel = datenmodell[0]
self.fenster = Tk()
self.fenster.title("Ampel")
self.fenster.geometry("400x300")
self.canvas = Canvas(master=self.fenster)
self.canvas.place(x=0, y=0, width=400, height=300)
self.hintergrundbild = PhotoImage(file="hintergrund.gif")
self.canvas.create_image(0, 0, image=self.hintergrundbild, anchor=NW)
self.canvas.create_rectangle(250, 120, 262, 152, fill=grau)
self.id_rot = self.canvas.create_oval(252, 122, 260, 130, fill=grau)
self.id_gelb = self.canvas.create_oval(252, 132, 260, 140, fill=grau)
self.id_gruen = self.canvas.create_oval(252, 142, 260, 150, fill=grau)
self.canvas.create_rectangle(255, 152, 257, 184, fill=grau)
self.anzeigeAktualisieren()
self.buttonWeiter = Button(master=self.fenster,
text="weiter",
command=self.buttonWeiterClick)
self.buttonWeiter.place(x=150, y=270, width=100, height=20)
def buttonWeiterClick(self):
self.ampel.schalten()
self.anzeigeAktualisieren()
def anzeigeAktualisieren(self):
(lampeRot, lampeGelb, lampeGruen) = ampel.getLampen()
if lampeRot:
self.canvas.itemconfigure(self.id_rot, fill=rotAn)
else:
self.canvas.itemconfigure(self.id_rot, fill=rotAus)
if lampeGelb:
self.canvas.itemconfigure(self.id_gelb, fill=gelbAn)
else:
self.canvas.itemconfigure(self.id_gelb, fill=gelbAus)
if lampeGruen:
self.canvas.itemconfigure(self.id_gruen, fill=gruenAn)
else:
self.canvas.itemconfigure(self.id_gruen, fill=gruenAus)
ampel = Ampel('rot')
datenmodell = [ampel]
gui = GUI(datenmodell)
gui.fenster.mainloop()
Das Objekt gui
nutzt hier eine Strategie, die man Befragen oder Polling nennt. Das Objekt gui
weiß
genau, wann sich das Datenmodell ändert (nach jedem Anklicken einer Schaltfläche) und besorgt sich
dann die geänderten Daten zur Anzeige auf den vorgesehenen Labels.
Aufgabe 1
Erkläre das Verhalten der Objekte anhand des folgenden Sequenzdiagramms.
Das Datenmodell beobachten
In der folgenden Implementierung wird eine andere Strategie benutzt, um veränderte Daten an das Objekt gui
weiterzugeben.
class Ampel(object):
def __init__(self, anfangszustand):
self.zustand = anfangszustand
self.beobachter = None
def setBeobachter(self, pBeobachter):
self.beobachter = pBeobachter
def setZustand(self, z):
self.zustand = z
self.beobachter.anzeigeAktualisieren()
def getZustand(self):
return self.zustand
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.beobachter.anzeigeAktualisieren()
def getLampen(self):
if self.zustand == 'rot':
lampen = (True, False, False)
elif self.zustand == 'rotgelb':
lampen = (True, True, False)
elif self.zustand == 'gruen':
lampen = (False, False, True)
elif self.zustand == 'gelb':
lampen = (False, True, False)
return lampen
from tkinter import *
grau = '#404040'
rotAn = '#FF0000'
rotAus = '#550000'
gelbAn = '#FFFF00'
gelbAus = '#555500'
gruenAn = '#00FF00'
gruenAus = '#005500'
class GUI(object):
def __init__(self, datenmodell):
self.ampel = datenmodell[0]
self.fenster = Tk()
self.fenster.title("Ampel")
self.fenster.geometry("400x300")
self.canvas = Canvas(master=self.fenster)
self.canvas.place(x=0, y=0, width=400, height=300)
self.hintergrundbild = PhotoImage(file="hintergrund.gif")
self.canvas.create_image(0, 0, image=self.hintergrundbild, anchor=NW)
self.canvas.create_rectangle(250, 120, 262, 152, fill=grau)
self.id_rot = self.canvas.create_oval(252, 122, 260, 130, fill=grau)
self.id_gelb = self.canvas.create_oval(252, 132, 260, 140, fill=grau)
self.id_gruen = self.canvas.create_oval(252, 142, 260, 150, fill=grau)
self.canvas.create_rectangle(255, 152, 257, 184, fill=grau)
self.buttonWeiter = Button(master=self.fenster,
text="weiter",
command=self.buttonWeiterClick)
self.buttonWeiter.place(x=150, y=270, width=100, height=20)
def buttonWeiterClick(self):
self.ampel.schalten()
def anzeigeAktualisieren(self):
(lampeRot, lampeGelb, lampeGruen) = ampel.getLampen()
if lampeRot:
self.canvas.itemconfigure(self.id_rot, fill=rotAn)
else:
self.canvas.itemconfigure(self.id_rot, fill=rotAus)
if lampeGelb:
self.canvas.itemconfigure(self.id_gelb, fill=gelbAn)
else:
self.canvas.itemconfigure(self.id_gelb, fill=gelbAus)
if lampeGruen:
self.canvas.itemconfigure(self.id_gruen, fill=gruenAn)
else:
self.canvas.itemconfigure(self.id_gruen, fill=gruenAus)
ampel = Ampel(None)
datenmodell = [ampel]
gui = GUI(datenmodell)
ampel.setBeobachter(gui)
ampel.setZustand('rot')
gui.fenster.mainloop()
Das Objekt gui
nutzt hier eine Strategie, die man Beobachten nennt. Das Objekt gui
wird als registriertes beobachtendes Objekt des Datenmodell-Objekts jeweils benachrichtigt, wenn sich das Datenmodell verändert hat.
Hierzu ist die Klasse Ampel
um ein Attribut beobachter
erweitert worden. Dieses Referenzattribut verwaltet zur Laufzeit einen Zeiger auf ein GUI
-Objekt. Wenn sich Daten im Ampel
-Objekt verändern, so wird das GUI
-Objekt angewiesen, die Anzeige zu aktualisieren.
Beachte, dass die Klassen Ampel
und GUI
so gestaltet sind, dass die Trennung zwischen Datenmodell und GUI weiterhin Bestand hat. Erst zur Laufzeit wird der Beobachter
des Datenmodellobjekts festgelegt. Bei der Modellierung wird ein Beobachter mit Hilfe eines Referenzattributs vorgesehen, aber noch nicht konkretisiert.
Aufgabe 2
Erkläre das Verhalten der Objekte anhand des folgenden Sequenzdiagramms.