Model-View-Control-Architektur
Weitere Aufteilung von Zuständigkeiten
In der Datenmodell-GUI-Architektur ist das GUI-Objekt sowohl für die Präsentation der Daten auf der Benutzeroberfläche als auch für die Ereignisverarbeitung zuständig.
Diese beiden Zuständigkeiten sollen jetzt aufgeteilt werden: Einerseits soll es Präsentationsobjekte geben, die nur für die Darstellung der Daten zuständig sind, andererseits ein Steuerungsobjekt, das die Verbindungen zwischen Präsentationsobjekten und Datenmodellobjekten herstellt und die Ereignisverarbeitung festlegt. Diese weitere Aufteilung der Zuständigkeiten soll spätere Änderungen oder Erweiterungen des Softwaresystems erleichtern und eine Wiederverwendbarkeit einzelnen Komponenten ermöglichen.
Grafische Benutzeroberfläche
Wir betrachten hier noch einmal die folgende einfache grafische Benutzeroberfläche:
Das Programm zu dieser grafischen Benutzeroberfläche kann auch aus den folgenden Softwarebausteinen zusammengesetzt werden.
Datenmodell (model)
Das Datenmodell bleibt unverändert.
#------------------------------------------------------------------------------
# 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
Präsentation (view)
Aufgabe eines GUI
-Objekts ist es, die grafische Benutzeroberfläche zu erzeugen.
#------------------------------------------------------------------------------
# GUI
#------------------------------------------------------------------------------
from tkinter import *
# Farben
grau = '#404040'
rotAn = '#FF0000'
rotAus = '#550000'
gelbAn = '#FFFF00'
gelbAus = '#555500'
gruenAn = '#00FF00'
gruenAus = '#005500'
class GUI(object):
def __init__(self, cbEreigninsverarbeitung):
# Referenzattribute zum Datenmodell
self.cbWeiterClick = cbEreigninsverarbeitung[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.cbWeiterClick)
self.buttonWeiter.place(x=150, y=270, width=100, height=20)
Beachte, dass ein GUI
-Objekt keinen Zugriff auf Datenmodell-Objekte hat.
Das ist deshalb möglich, weil hier keine Ereignisverarbeitungsmethoden implementiert sind.
Die für die Verarbeitung der Ereignisse erforderlichen Ereignisverarbeitungsmethoden
werden dem GUI
-Objekt übergeben.
Hier wird ein Mechanismus benutzt, den man Callback-Funktion
nennt.
Es werden Zeiger auf an anderer Stelle festgelegte Funktionsdefinitionen übergeben.
Steuerung (control)
Aufgabe eines Controller
-Objekts ist es, die Datenmodell- und GUI-Objekte
zu erzeugen und zu verknüpfen.
Beachte, dass ein Controller
-Objekt Zugriff auf Datenmodell- und Präsentationsobjekte hat.
Beachte auch, dass ein Controller
-Objekt die Ereignisverarbeitungsmethoden implementiert,
die dem GUI
-Objekt bei seiner Erzeugung übergeben werden.
#-----------------------------------------------------------
# Controller
#-----------------------------------------------------------
from ampel import *
from gui_ampel import *
class Controller(object):
def __init__(self):
# Erzeugung der Datenmodell-Objekte
self.ampel = Ampel('rot')
# Erzeugung und Initialisierung der GUI
ereignisverarbeitung = [self.weiterClick,]
self.gui = GUI(ereignisverarbeitung)
# Initialisierung von Ausgaben
self.anzeigeAktualisieren()
# Ereignisschreife
self.gui.fenster.mainloop()
def weiterClick(self):
self.ampel.schalten()
self.anzeigeAktualisieren()
def anzeigeAktualisieren(self):
(lampeRot, lampeGelb, lampeGruen) = self.ampel.getLampen()
if lampeRot:
self.gui.canvas.itemconfigure(self.gui.id_rot, fill=rotAn)
else:
self.gui.canvas.itemconfigure(self.gui.id_rot, fill=rotAus)
if lampeGelb:
self.gui.canvas.itemconfigure(self.gui.id_gelb, fill=gelbAn)
else:
self.gui.canvas.itemconfigure(self.gui.id_gelb, fill=gelbAus)
if lampeGruen:
self.gui.canvas.itemconfigure(self.gui.id_gruen, fill=gruenAn)
else:
self.gui.canvas.itemconfigure(self.gui.id_gruen, fill=gruenAus)
# Erzeugung des Controllers
controller = Controller()
MVC-Architektur
Das folgende Klassendiagramm verdeutlicht noch einmal die Beziehungen zwischen den Komponenten Datenmodell (Model), Präsentation (View) und Steuerung (Control).
Vereinfacht lässt sich die MVC-Architektur wie folgt darstellen.
Hier erfolgt eine strikte Trennung des Datenmodells (Model) von der grafischen Präsentation (View). Die Komponenten Model und View wissen nichts voneinander. Die notwendigen Verbindungen werden von einer dritten Komponente, dem Controller, ausgeführt. Der Controller braucht dazu eine präzise Beschreibung der Schnittstellen von Model und View.
Hinweis
Die hier gezeigte Darstellung der MVC-Architektur orientiert sich sehr stark an den Ausführungen auf den Seiten des HSG.