i

Befrager-Architektur / Beobachter-Architektur

Das Datenmodell befragen

Wir betrachten zunächst noch einmal ein Programm zur Simulation der Ampel.

#------------------------------------------------------------------------------
# Datenmodell
#------------------------------------------------------------------------------

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

------------------------------------------------------------------------------

GUI-Klasse

------------------------------------------------------------------------------

from tkinter import *

Farben

grau = '#404040'
rotAn = '#FF0000'
rotAus = '#550000'
gelbAn = '#FFFF00'
gelbAus = '#555500'
gruenAn = '#00FF00'
gruenAus = '#005500'

class GUI(object):
def init(self, datenmodell):

Referenzattribute zum Datenmodell

    self.ampel = datenmodell[0]
    # Erzeugung des Fensters
    self.fenster = Tk()
    self.fenster.title("Ampel")
    self.fenster.geometry("400x300")
    # Zeichenfläche
    self.canvas = Canvas(master=self.fenster)
    self.canvas.place(x=0, y=0, width=400, height=300)
    # Hintergrundbild
    self.hintergrundbild = PhotoImage(file="hintergrund.gif")
    self.canvas.create_image(0, 0, image=self.hintergrundbild, anchor=NW)
    # Ampelanzeige
    # Ampelkasten
    self.canvas.create_rectangle(250, 120, 262, 152, fill=grau)
    # Rot-Licht
    self.id_rot = self.canvas.create_oval(252, 122, 260, 130, fill=grau)
    # Gelb-Licht
    self.id_gelb = self.canvas.create_oval(252, 132, 260, 140, fill=grau)
    # Grün-Licht
    self.id_gruen = self.canvas.create_oval(252, 142, 260, 150, fill=grau)
    # Stange
    self.canvas.create_rectangle(255, 152, 257, 184, fill=grau)
    # Aktualisierung der Anzeige
    self.anzeigeAktualisieren()
    # Button zum Auswerten
    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):
    # Verarbeitung der Daten
    self.ampel.schalten()
    # Aktualisierung der Anzeige
    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)

------------------------------------------------------------------------------

Erzeugung der Objekte

------------------------------------------------------------------------------

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.

Sequenzdiagramm

Das Datenmodell beobachten

In der folgenden Implementierung wird eine andere Strategie benutzt, um veränderte Daten an das Objekt gui weiterzugeben.

#------------------------------------------------------------------------------
# Datenmodell
#------------------------------------------------------------------------------

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

------------------------------------------------------------------------------

GUI-Klasse

------------------------------------------------------------------------------

from tkinter import *

Farben

grau = '#404040'
rotAn = '#FF0000'
rotAus = '#550000'
gelbAn = '#FFFF00'
gelbAus = '#555500'
gruenAn = '#00FF00'
gruenAus = '#005500'

class GUI(object):
def init(self, datenmodell):

Referenzattribute zum Datenmodell

    self.ampel = datenmodell[0]
    # Erzeugung des Fensters
    self.fenster = Tk()
    self.fenster.title("Ampel")
    self.fenster.geometry("400x300")
    # Zeichenfläche
    self.canvas = Canvas(master=self.fenster)
    self.canvas.place(x=0, y=0, width=400, height=300)
    # Hintergrundbild
    self.hintergrundbild = PhotoImage(file="hintergrund.gif")
    self.canvas.create_image(0, 0, image=self.hintergrundbild, anchor=NW)
    # Ampelanzeige
    # Ampelkasten
    self.canvas.create_rectangle(250, 120, 262, 152, fill=grau)
    # Rot-Licht
    self.id_rot = self.canvas.create_oval(252, 122, 260, 130, fill=grau)
    # Gelb-Licht
    self.id_gelb = self.canvas.create_oval(252, 132, 260, 140, fill=grau)
    # Grün-Licht
    self.id_gruen = self.canvas.create_oval(252, 142, 260, 150, fill=grau)
    # Stange
    self.canvas.create_rectangle(255, 152, 257, 184, fill=grau)
    # Button zum Auswerten
    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):
    # Verarbeitung der Daten
    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)

-----------------------------------------------------------

Datenmodell

-----------------------------------------------------------

ampel = Ampel(None)
datenmodell = [ampel]

-----------------------------------------------------------

GUI-Objekt

-----------------------------------------------------------

gui = GUI(datenmodell)

Beobachter festlegen

ampel.setBeobachter(gui)
ampel.setZustand('rot')

Ereignisschleife starten

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.

Sequenzdiagramm

Suche

v
3.2.1.6.1.3
www.inf-schule.de/modellierung/ooppython/ampel/gui/erkundung_gui/beobachter

Rückmeldung geben