Exkurs - Model-View-Control-Architektur
Weitere Aufteilung von Zuständigkeiten
Bisher war 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 Steuerungsobjekte, die die Verbindungen zwischen Präsentationsobjekten und Datenmodellobjekten herstellen und die Ereignisverarbeitung festlegen. 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 zur Bankwelt
Wir betrachten hier noch einmal die folgende sehr einfache grafische Benutzeroberfläche:
Das Programm mit dieser grafischen Benutzeroberfläche wird mit den folgenden Softwarebausteinen erzeugt.
Datenmodell (model)
Das Datenmodell bleibt unverändert.
class Konto(object):
def __init__(self, nummer):
self.nr = nummer
self.stand = 0.0
self.inhaber = None
...
class Kunde(object):
def __init__(self, name, vorname, pin):
self.name = name
self.vorname = vorname
self.konto= None
...
class Bank(object):
def __init__(self):
self.konten = []
self.kunden = []
self.naechsteKontoNr = 0
...
Präsentation (view)
Aufgabe eines GUIBank
-Objekts ist es, die grafische Benutzeroberfläche zu erzeugen.
from tkinter import *
class GUIBank():
def __init__(self, cbErzeugen, cbAnzeigen, cbEinzahlen, cbAuszahlen, cbUeberweisen):
# Referenzattribute zum Datenmodell
self.cbErzeugen = cbErzeugen
self.cbAnzeigen = cbAnzeigen
self.cbEinzahlen = cbEinzahlen
self.cbAuszahlen = cbAuszahlen
self.cbUeberweisen = cbUeberweisen
# Erzeugung des Fensters
self.fenster = Tk()
self.fenster.title("Bank")
self.fenster.geometry("455x275")
# Rahmen Kontoerzeugung
self.rahmenErzeugen = Frame(master=self.fenster, background="#FFCFC9")
self.rahmenErzeugen.place(x=5, y=5, width=220, height=130)
# Button Erzeugen
self.buttonErzeugen = Button(master=self.rahmenErzeugen, text="erzeugen",
command=self.cbErzeugen)
self.buttonErzeugen.place(x=5, y=5, width=145, height=20)
# Label mit Aufschrift Name
self.labelName = Label(master=self.rahmenErzeugen, background="white", text="Name")
self.labelName.place(x=5, y=30, width=145, height=20)
# Entry für den Namen
self.entryName = Entry(master=self.rahmenErzeugen)
self.entryName.place(x=155, y=30, width=60, height=20)
# ...
Beachte, dass ein GUIBank
-Objekt keinen Zugriff auf Objekte des Datenmodells hat.
Das ist deshalb möglich, weil hier keine Ereignisverarbeitungsmethoden implementiert sind.
Die für die Verarbeitung des Ereignisses Anklicken der Schaltfläche mit der Aufschrift anzeigen
erforderliche Ereignisverarbeitungsmethode wird erst bei der Aktualisierung des Parameters
cbAnzeigen
- also bei der Erzeugung des GUIBank
-Objekts - festgelegt.
Hier wird ein Mechanismus benutzt, den man Callback-Funktion
nennt. Mehr über Callback-Funktionen
findest du im Abschnitt
Fachkonzept - Callback-Funktion
Steuerung (control)
Aufgabe eines Controler
-Objekts ist es, die Datenmodell- und GUI-Objekte
zu erzeugen und zu verknüpfen.
Beachte, dass ein Controler
-Objekt Zugriff auf Datenmodell- und Präsentationsobjekte hat.
Beachte auch, dass ein Controler
-Objekt die Ereignisverarbeitungsmethoden implementiert,
die dem GUIBank
-Objekt bei seiner Erzeugung übergeben werden.
from model_bank import *
from view_bank import *
class Controler(object):
def __init__(self):
# Erzeugung der Datenmodell-Objekte
self.bank = Bank()
# Erzeugung und Initialisierung der GUI
self.guibank = GUIBank(self.buttonErzeugen,
self.buttonAnzeigen,
self.buttonEinzahlen,
self.buttonAuszahlen,
self.buttonUeberweisen)
# Initialisierung von Ausgaben
self.guibank.labelKontonummer.config(text=str(self.bank.naechsteKontoNr))
# Ereignisschreife
self.guibank.fenster.mainloop()
def buttonErzeugen(self):
name = self.guibank.entryName.get()
vorname = self.guibank.entryVorname.get()
self.bank.kontoEroeffnen(name, vorname)
self.guibank.entryName.delete(0, END)
self.guibank.entryVorname.delete(0, END)
self.guibank.labelKontonummer.config(text=str(self.bank.naechsteKontoNr))
self.guibank.labelKontostandErzeugen.config(text=str(0.0))
def buttonAnzeigen(self):
kontonummer = int(self.guibank.entryKontonummerAnzeigen.get())
konto = self.bank.getKonto(kontonummer)
if konto != None:
self.guibank.labelNameAnzeigen.config(text=str(konto.inhaber.name))
self.guibank.labelVornameAnzeigen.config(text=str(konto.inhaber.vorname))
self.guibank.labelKontostand.config(text=str(konto.stand))
else:
self.guibank.labelNameAnzeigen.config(text='')
self.guibank.labelVornameAnzeigen.config(text='')
self.guibank.labelKontostand.config(text='')
def buttonEinzahlen(self):
kontonummer = int(self.guibank.entryKontonummer.get())
betrag = float(self.guibank.entryEinzahlungsbetrag.get())
self.bank.einzahlen(kontonummer, betrag)
self.guibank.entryKontonummer.delete(0, END)
self.guibank.entryEinzahlungsbetrag.delete(0, END)
def buttonAuszahlen(self):
kontonummer = int(self.guibank.entryKontonummer.get())
betrag = float(self.guibank.entryAuszahlungsbetrag.get())
self.bank.auszahlen(kontonummer, betrag)
self.guibank.entryKontonummer.delete(0, END)
self.guibank.entryAuszahlungsbetrag.delete(0, END)
def buttonUeberweisen(self):
kontonummer = int(self.guibank.entryKontonummer.get())
zielkontonummer = int(self.guibank.entryZielkontonummer.get())
betrag = float(self.guibank.entryUeberweisungsbetrag.get())
self.bank.ueberweisen(kontonummer, zielkontonummer, betrag)
self.guibank.entryKontonummer.delete(0, END)
self.guibank.entryZielkontonummer.delete(0, END)
self.guibank.entryUeberweisungsbetrag.delete(0, END)
controler = Controler()
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.