Entwurfsmuster

Erstelle für diese Übung einen Fork vom Repository m426-2026/design-patterns und klone diesen.

Der Seitenbereich Design Patterns von refactoring.guru enthält hilfreiche Informationen zum Lösen der folgenden Aufgaben.

Aufgabe 0: Geometry

Entwurfsmuster: Adapter (structural)

Hierbei handelt es sich um das Beispiel von den Folien, jedoch von C# auf TypeScript umgeschrieben.

Studiere den Code in den Dateien geometry.ts, geometry_third_party.ts und geometry_test.ts.

Erweitere anschliessend die Klasse Calculator um eine weitere Methode getWidthHeightRatio(Rectangular), welche das Vehältnis der Breite zur Höhe des mitgegebenen rechteckigen Objekts berechnet. Bei Quadraten müsste das Verhältnis jeweils 1.0 betragen.

Aufgabe 1: Parkhaussimulation

Entwurfsmuster: Observer (behavioral)

In parking_lot.ts und parking_lot_simulation.ts ist eine Parkhaussimulation implementiert, die folgendermassen gestartet werden kann:

deno parking_lot_simulation.ts

Ein Parkhaus (ParkingLot) hat einen Namen (name), eine Kapazität (capacity) und einen Füllstand (occupied). Mithilfe der Methoden enter() und exit() kann ein Auto in das Parkhaus einfahren bzw. dieses verlassen.

In index.ts wird das Ein- und Ausfahren von Autos mit asynchronen Funktionen simuliert. Die Funktion display zeigt dabei mittels Polling (Abfrage in regelmässigen Intervallen) den Füllstand an.

Baue die Simulation foldendermassen um:

  1. Das Parkhaus soll als Publisher agieren und seine Subscriber jeweils informieren, wenn ein Auto in das Parkhaus einfährt oder es verlässt.
    • Erstelle hierzu zwei Interfaces Publisher und Subscriber mit den entsprechenden Methoden.
  2. Eine neue Klasse namens Display soll als Subscriber agieren, welcher über ein- und ausfahrende Autos des Parkhauses informiert wird.
  3. Überlege dir, ob die Ereignisse als eine Reihe von Variablen (Name des Parkhauses, Füllstand usw.) übermittelt werden sollen, oder ob hier ein spezielles Event-Objekt hilfreich wäre.
  4. Ein- und ausfahrende Autos einerseits und der Füllstand des Parkhauses andererseits sollen nicht mehr in getrennten Meldungen ausgegeben werden, sondern zusammen als eine Meldung:

Alt:

A car entered the lot Bahnhof Parking.
Bahnhof Parking: 3/100 occupied
Bahnhof Parking: 3/100 occupied
Bahnhof Parking: 3/100 occupied
A car left the lot Bahnhof Parking.
Bahnhof Parking: 2/100 occupied
Bahnhof Parking: 2/100 occupied

Neu:

A car entered the lot Bahnhof Parking: 3/100 occupied.
A car left the lot Bahnhof Parking: 2/100 occupied.

Diese kompakte Ausgabe ist nur möglich, wenn auf Polling (d.h. Anfragen in bestimmten Intervallen) verzichtet wird und man stattdessen einen Mechanismus wie einen Observer verwendet.

Aufgabe 2: Texteditor

Entwurfsmuster: State (behavioral)

In texteditor.ts und in texteditor.html ist ein Text-Editor implementiert, der Texteingaben als Pseudo-Dateien im localStorage des Browsers ablegt.

Ein Text kann entweder einem Dateinamen zugeordnet sein (“saved”) oder nicht (“unsaved”). Gegenüber der letzten Speicherung kann der Text eine Änderung erfahren haben (“dirty”) oder keine Veränderung haben (“clean”). Aus diesen beiden Aspekten ergeben sich folgende Zustände, die in der enum State definiert sind:

  • CleanUnsaved: keine Veränderung, kein Dateiname vorhanden
  • CleanSaved: keine Veränderung, Dateiname vorhanden
  • DirtyUnsaved: mit Veränderung, kein Dateiname vorhanden
  • DirtySaved: mit Veränderung, Dateiname vorhanden

Mit der Variable state wird der jeweilige Zustand des Editors festgehalten. Verschiedene Stellen im Code reagieren unterschiedlich auf diesen Zustand. Beispiel: Bei *Unsaved-Zuständen muss beim Betätigen der Save-Schaltfläche ein Dateiname angegeben werden, bei *Saved-Zuständen hingegen wird der zuletzt verwendete Dateinamen verwendet.

Verbessere den Quellcode, indem du das Sate-Entwurfsmuster implementierst. Aus der enum State wird ein Interface, das durch die vier bereits bekannten Zuständen als Klassen implementiert wird. Die zustandsabhängigen Daten (z.B. der Dateiname) sollen dann Teil der jeweiligen Zustands-Instanz sein, und nicht mehr im Editor-Code als globale Variablen herumliegen.

Wichtig: Der TypeScript-Code muss nach jeder Änderung nach JavaScript transpiliert werden. Hierzu ist ein Deno-Task vorkonfiguriert, der folgendermassen gestartet werden kann:

deno task texteditor

Nimm deine Änderungen nur im TypeScript-Code (texteditor.ts) vor und nicht im generierten JavaScript-Code (texteditor.js).