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:

GUI

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).

Klassendiagramm

Vereinfacht lässt sich die MVC-Architektur wie folgt darstellen.

Klassendiagramm

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.

X

Fehler melden

X

Suche