Patterns in der Softwarearchitektur: Model-View-Controller

Der Model-View-Controller ist eines der klassischen Architekturmuster für flexible Mensch-Maschine-Schnittstellen.

In Pocket speichern vorlesen Druckansicht

(Bild: Blackboard/Shutterstock.com)

Lesezeit: 4 Min.
Von
  • Rainer Grimm
Inhaltsverzeichnis

Patterns sind eine wichtige Abstraktion in der modernen Softwareentwicklung und Softwarearchitektur. Sie bieten eine klar definierte Terminologie, eine saubere Dokumentation und das Lernen von den Besten.Der Model-View-Controller (MVC) ist eines der klassischen Architekturmuster aus dem Buch "Pattern-Oriented Software Architecture, Volume 1". Es richtet sich an interaktive Anwendungen mit einer flexiblen Mensch-Maschine-Schnittstelle.

Modernes C++ – Rainer Grimm

Rainer Grimm ist seit vielen Jahren als Softwarearchitekt, Team- und Schulungsleiter tätig. Er schreibt gerne Artikel zu den Programmiersprachen C++, Python und Haskell, spricht aber auch gerne und häufig auf Fachkonferenzen. Auf seinem Blog Modernes C++ beschäftigt er sich intensiv mit seiner Leidenschaft C++.

Das MVC unterteilt die Programmlogik einer Bedienoberfläche in die einzelnen Komponenten Model, View und Controller. Das Model verwaltet die Daten und Regeln der Anwendung. Die View stellt die Daten dar, und der Controller interagiert mit dem Benutzer.

Zweck

  • Benutzeroberflächen müssen häufig geändert werden,
  • unterschiedliche Benutzeroberflächen müssen unterstützt werden und
  • das Datenmodell ist stabil.

Lösung

  • Die Anwendung wird in die Komponenten Model (Datenmodell), View (Ausgabekomponenten) und Controller (Eingabekomponenten) unterteilt und
  • mehrere Ausgabekomponenten können das gleiche Datenmodell verwenden.

Struktur

Model

  • ist die zentrale Komponente des Musters,
  • enthält die Daten (und die Geschäftslogik) und
  • ist unabhängig von View und Controller.

View

  • ist für die Darstellung der Daten und die Benutzerinteraktion verantwortlich
  • Beobachtet das Model und
  • eine View ist mit einem Controller verbunden, um das Model zu manipulieren.

Controller

  • verwaltet eine oder mehrere Views
  • nimmt Benutzerinteraktionen entgegen und bereitet sie für das Model oder View auf,
  • beobachtet das Model und
  • implementiert die Aktualisierungslogik.

Es gibt zwei interessante Aspekte der MVC: Initialisierung und Benutzereingabe:

Initialisierung

Die folgenden Schritte finden während der Initialisierung des MVC statt:

  • Das Modell wird erstellt und seine Daten werden initialisiert.
  • Die Views werden erstellt und beobachten das Modell.
  • Der Controller wird erstellt und erhält Verweise auf das Model und die Views.
  • Der Controller beobachtet das Model.
  • Die Anwendung beginnt mit der Ereignisverarbeitung.

Benutzereingabe

Bei einem Benutzerereignis finden die folgenden Schritte statt:

  • Der Controller nimmt die Benutzereingaben entgegen, verarbeitet sie und triggert das Modell.
  • Das Modell ändert seine internen Daten.
  • Das Modell benachrichtigt alle Views und Controller über die Änderung der internen Daten.
  • Die Views und Controller aktualisieren sich selbst. Beispiel: Wenn die PIN an einem Geldautomaten zum dritten Mal falsch eingegeben wird, kann das bedeuten: Auf dem Display wird angezeigt, dass das Konto gesperrt ist. Der Geldautomat beschlagnahmt die Debitkarte.
  • Der Controller fährt fort, Ereignisse zu verarbeiten.

Das folgende Programm mvc.cpp wendet MVC an.

// mvc.cpp

#include <iostream>
#include <string>
#include <unordered_map>


class DefectModel {
 public:                                                   // (5)
     mutable std::unordered_map<std::string, std::string>
       defects_ = { {"XYZ" , "File doesn't get deleted."},
                    {"XAB" , "Registry doesn't get created."},
                    {"ABC" , "Wrong title get displayed."} };

    std::string getDefectComponent(const std::string& component) 
      const {
        return defects_[component];
    }

    int getSummary() const {
        return defects_.size();
    }

    std::unordered_map<std::string, std::string> getAllDefects() 
      const {
        return defects_;
    }

};

class DefectView {
 public:
    void showSummary(int num) const {
        std::cout << "There are " + std::to_string(num) + 
          " defects in total!\n";
    }
    
    void showDefectComponent(const std::string& defect) const {
        std::cout << "Defect of component: " + defect + '\n';
    }

    void showDefectList(const std::unordered_map<std::string, 
                        std::string>& defects) const {
        for (const auto& p: defects) {
            std::cout << "(" + p.first + ", " + p.second + ")\n";
        }
    }
};

class DefectController {
    const DefectModel& defectModel;
    const DefectView& defectView;
 public:
    DefectController(const DefectModel& defModel, 
                     const DefectView& defView):
      defectModel{defModel}, defectView{defView} { }

    void showDefectComponent(const std::string& component) 
      const {
        defectView.showDefectComponent(
          defectModel.getDefectComponent(component));    // (6)
    }

    void showDefectSummary() const {
      defectView.showSummary(defectModel.getSummary());  // (7)
    }

    void showDefectList() const {
      defectView.showDefectList(
        defectModel.getAllDefects());                    // (8)
    }

};

int main() {

    std::cout << '\n';

    DefectModel defectModel;
    DefectView defectView;

    
    DefectController defectController(defectModel, 
                                      defectView);  // (1)
    defectController.showDefectComponent("ABC");    // (2)
    std::cout << '\n';
    defectController.showDefectSummary();           // (3)
    std::cout << '\n';
    defectController.showDefectList();              // (4)

    std::cout << '\n';

}

Der Controller holt sich sein Modell und seine Ansicht in seinem Konstruktor (1) und zeigt seine Fehlerliste defects_ (5) auf drei Arten an (2 - 4). Der Controller stößt jeden Aufruf in der main Funktion an und verwendet seine Views, um die vom Modell vorgegebenen Daten anzuzeigen (6 - 8).

Der folgende Screenshot zeigt die Ausgabe des Programms:

Variation

Presentation-Abstraction-Control ist ein weiteres Muster aus dem Buch "Pattern-Oriented Software Architecture, Volume 1 und ähnelt dem MVC. Es verwendet eine hierarchische Struktur von Agenten, wobei jeder Agent aus der Präsentation, der Abstraktion (Modell) und der Datenkontrolle besteht. Die Agenten kommunizieren miteinander mithilfe des Controllers.

Vorteile

  • Separation of concern: Das Modell ist strikt vom Controller und den Views getrennt. Daher können viele Views oder Controller gleichzeitig unterstützt und zur Laufzeit geändert werden.
  • Die Views sind synchronisiert, da sie zur gleichen Zeit aktualisiert werden.

Nachteile

  • MVC kann für eine kleine Mensch-Maschine-Schnittstelle zu komplex und überladen sein.
  • Eine Änderung am Modell kann eine Kaskade von Operationen an den abhängigen Views und Controllern auslösen.
  • Der View und der Controller sind stark gekoppelt. Eine Änderung an einem von ihnen kann den anderen zerstören.

Ereignisgesteuerte Anwendungen wie GUIs oder Server verwenden oft das Architekturmuster Reactor. Ein Reactor kann mehrere Anfragen gleichzeitig annehmen und sie auf verschiedene Handler verteilen. (rme)