Task B: Model-View-Control Pattern#

In dieser Aufgabe sollen sie den bisherigen einfachen ChatBot aus Task A refaktorisieren und in besser strukturierten Code umsetzen. In den einzelnen Teilbeispielen (B 1 bis B 3) wird dabei jeweils die Musterlösung des ngram-Bots genutzt. Sie können dies aber natürlich auch erweitern auf die weiter fortgeschrittenen Beispiele aus Task A (z.B. die ChatGPT Anbindung). Dies soll in dem Design-Pattern [Gamma et al., 1994] Model-View-Controller umgesetzt werden. Zuerst sollen in diesem Task diese drei einzelnen Bestandteile in dem Beispiel nachvollzogen werden und dann entsprechend erweitert werden.

Model-View-Controller Design-Pattern#

Das Model-View-Controller (MVC) Design-Pattern wird zur klaren Trennung in unterschiedliche Funktionalitäten genutzt, mit dem Ziel der Wiederverwendbarkeit der so sich ergebenden einzelnen Bausteine [Deacon, 2009]. Es hilft, den Code in verschiedene Komponenten aufzuteilen, um eine saubere Trennung von Verantwortlichkeiten und eine bessere Wartbarkeit der Anwendung zu ermöglichen.

Die drei Hauptkomponenten des MVC-Modells sind das Modell, die Ansicht und der Controller.

  • Modell: repräsentiert die Daten und die Logik der Anwendung. Es enthält Daten und Methoden, die verwendet werden, um diese Daten zu verwalten und zu aktualisieren. Das Modell wird normalerweise von einer Datenbank abstrahiert und kann aus mehreren Klassen bestehen. Es ist unabhängig von Ansichten und der Ablaufsteuerung.

  • Ansicht / View: repräsentiert die Benutzeroberfläche der Anwendung. Es zeigt dem Benutzer die Daten aus dem Modell an und ermöglicht ihm die Interaktion mit der Anwendung. Änderungen am Modell werden meist über ein Observer-Pattern auf dem Modell wahrgenommen und dann in der Ansicht entsprechend aktualisiert und angepasst dargestellt.

  • Controller: übernimmt die Programmsteuerung. Es empfängt Eingaben von dem/der Benutzer:in (heute ist dies häufig an spezielle Views gekoppelt) und verwendet diese Eingaben, um das Modell zu aktualisieren. Dient häufig so als Schnittstelle zwischen dem Modell und der Ansicht.

../../_images/MVC-Prozess_01.png

Durch die Verwendung des MVC-Ansatzes können Entwickler:innen die Komponenten der Anwendung isoliert voneinander testen und wiederverwendbare Komponenten erstellen. Der MVC-Ansatz ermöglicht es den Code zu modularisieren und so die Wartbarkeit der Anwendung zu verbessern. MVC ist beliebt in der Umsetzung von Computerspielen [Olsson, 2015].

Model-View-Adapter Design-Pattern#

Heutzutage werden vielfach Frameworks zur Darstellung in Computerprogrammen genutzt, die eigenen Interaktionskonzepten unterliegen und diese direkt realisieren. Daher ist die Verbindung der Ansicht/ View mit der Kontrolle – und dadurch der Art der Interaktion – sehr eng und diese sind direkt miteinander verwoben. Deshalb werden heute häufig Abwandlungen des MVC Patterns genutzt [Graca, 2017]. Eine Abwandlung des MVC Designmusters ist das Model-View-Adapter Design-Pattern [Lopez et al., 2012]. In diesem Konzept ist einer Ansicht/ View jeweils ein Adapter als Interface zum Modell direkt zugeordnet. So bleibt die Darstellung, in zum Beispiel einer Benutzeroberfläche, und die sich daraus ergebende Art der Interaktion austauschbar und ist unabhängig vom Modell. Gleichzeitig können aber so die besonderen Funktionalitäten genutzt werden, die eine spezielle Ansicht zur Interaktion anbietet.

Kompilieren und Ausführen der C++ Programme#

Sie können weiterhin auf dem JupyterHub arbeiten (Wählen sie dabei das “Software Development” Image (3.3.0)!). Hierbei wird direkt das entsprechende gitlab in ihren Arbeitsbereich geupdated. Auf dem Hub können sie eine IDE im Browser nutzen (siehe Beschreibung hier), wobei wir an diesem Termin mit einem einfachen Terminal auskommen.

Kompilieren und ausführen auf dem Hub:

  • Terminal auf dem Hub öffnen (im rechten Fenster oben einen neuen Tab öffnen, dort im Launcher ganz unten (unter “Other”) “Terminal” auswählen).

  • In das directory Task_B wechseln: cd Task_B und dort in die Unterverzeichnisse B_X.

  • Kompilieren über make

  • Starten über ./chatbot --input ../data/sample.txt

Der Code als Grundlage für die Task ist in diesem gitlab zu finden.

Eine sehr gute Referenz bietet https://cppreference.com/ und die C++ Übersicht an der Uni.

Ziele der Aufgaben#

  • Kennenlernen des Model-View-Controll Pattern

  • Anwenden einer einfachen graphischen Darstellungs-Library (wird eingeführt am 5.6.2024)

  • Struktur von Programmen verstehen

  • Wiederholen von grundlegenden Elementen zum Prozessfluss

Grundlegende Struktur#

Die einzelnen Teilaufgaben beschäftigen sich jeweils mit den unterschiedlichen Komponenten des MVC Patterns angewendet im Computerspiel Pong. Vorweg gestellt das aufrufende main Programm und zugehörige Makefile.

Dazu wird im MVC häufig ein observer genutzt. Hierfür wird ebenfalls der Header unten angegeben.

#include "control_chatbot.h"
#include "view_terminal.h"
#include "model_ngram_chatbot.h"
#include <iostream>
#include <string>

int main(int argc, char* argv[]) {
    /* Parsing input arguments for main method:
     * Required:
     *  --input   Input file in which the n-grams shall be counted
     */
    // Provide help on how to call main.
    if (argc < 3) {
        std::cerr << "Usage: " << argv[0] << " --input InputFile" << std::endl;
        return 1;
    }
    
    std::string input_file;
    // Parsing the arguments.
    for (int i = 1; i < argc; ++i) {
        std::string argument = argv[i];
        // Parsing for input file.
        if (argument == "--input") {
            if (i + 1 < argc) { // Make sure we aren't at the end of argv!
                input_file = argv[++i]; // Increment 'i' so we don't get the argument as the next argv[i].
            } else { // There was no argument to the input file option.
                std::cerr << "--input option requires one argument." << std::endl;
                return 1;
            }
        }
    }

    // Create an instance of the ChatBotModel
    ChatBotModel model;

    // Load the text and compute the bigrams
    model.loadTextAndComputeBigrams(input_file);  // Load and prepare the model

    // Controller is instantiated with a reference to the model
    ChatBotController controller(model);

    // Create the view, passing it the model and the controller
    ViewTerminal view(model, controller);

    // Run the view which starts the interaction loop
    view.run();
    return 0;
}
#ifndef OBSERVER_H_ // header guard to prevent multiple inclusions of the header file
#define OBSERVER_H_

#include <vector> // include the vector standard library, which will be used to store pointers to observers

// The observable mechanism is a simple mechanism that establishes a connection between two
// classes and standardizes this relation:
// - on the one hand is the observable class - the model is derived
//      and inherits from this: 
//          - a stack / list of so called observer objects
//          - a notifyUpdate method which informs these observers when something changes
//      Importantly, this notifyUpdate has to be called explicitly inside the Observable class.
// - on the other hand are possible observers. These have to implement an update() function
//      which is called in all connected observers when in the observable class notifyUpdate 
//      has been triggered.

class Observer // define the Observer class
{
public:
    virtual void update() = 0; // declare a pure virtual function called update(), which will be implemented by derived classes
};

class Observable // define the Observable class
{
    std::vector<Observer*> observers; // private vector to store pointers to registered observers

public:
    void addObserver(Observer* observer); // public function to add an observer to the vector of registered observers

    void notifyUpdate(); // public function to notify all registered observers that an update has occurred
};

#endif // end of header guard and file
CXX	:= c++
CXXFLAGS := -Wall -std=c++20

# Contain path for any includes (headers)
# Depending on your platform: Include a path to boost, on linux should be 
# /usr/local/include, on mac could be /opt/homebrew/include
INCLUDES := -I./include 

# Contains libraries we need to (-L is directory search path, -l is lib)
LDFLAGS = -L/usr/local/lib 
LDLIBS = #-lncurses

SRCDIR := ./src
BUILDDIR := ./build

CHATBOT_OBJECTS := $(addprefix $(BUILDDIR)/,main_ngram.o model_ngram_chatbot.o read_file.o count_ngram.o)

# Target executable for the chatbot
chatbot: $(CHATBOT_OBJECTS)
	$(CXX) $(LDFLAGS) $^ -o $@ $(LDLIBS)
    
$(BUILDDIR)/%.o: $(SRCDIR)/%.cpp
	@mkdir -p $(BUILDDIR)
	$(CXX) $(INCLUDES) $(CXXFLAGS) -c $< -o $@


clean:
	rm -rf $(BUILDDIR) chatbot
make
 ./chatbot --input ../data/sample.txt

Anweisungen

Gehen sie die Teilaufgaben einzeln durch: Über diese lernen sie die verschiedenen Teile (Model - Controller - View) kennen.

Referenzen#

1

John Deacon. Model-view-controller (mvc) architecture. Online][Citado em: 10 de março de 2006.] http://www. jdl. co. uk/briefings/MVC. pdf, 2009.

2

Erich Gamma, Richard Helm, Ralph Johnson, and John M. Vlissides. Design Patterns: Elements of Reusable Object-Oriented Software. Addison-Wesley Professional, 1 edition, 1994. ISBN 0201633612. URL: http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612/ref=ntt_at_ep_dpi_1.

3

H. Graca. MVC and its alternatives. Blogpost on https://herbertograca.com/2017/08/17/mvc-and-its-variants/, 2017. URL: https://herbertograca.com/2017/08/17/mvc-and-its-variants/ (visited on 2023).

4

Sheydi Anel Zamudio Lopez, Rene Santaolaya Salgado, and Olivia Graciela Fragoso Diaz. Restructuring object-oriented frameworks to model-view-adapter architecture. IEEE Latin America Transactions, 10(4):2010–2016, 2012.

5

Tobias Olsson. Evolution and evaluation of the model-view-controller architecture in games. In Proceedings of ICSE, 4th International Workshop on Games and Software EngineeringAt: Florence, Italy. 05 2015. doi:10.1109/GAS.2015.10.