Entwurfsmuster – was ist das?

Entwurfsmuster helfen dabei, wiederverwendbare Software zu schreiben. Dabei werden für häufig wiederkehrende Probleme bewährte Lösungsmuster angeboten. Entwurfsmuster sind aus der objektorientierten Programmierung nicht mehr wegzudenken.

Die Entwurfsmuster können in drei Hauptklassen eingeteilt werden.

Erzeugungsmuster

Sie beschreiben Strukturen, die den Prozess der Objekterzeugung enthalten. Das Anwendungsprogramm wird von der konkreten Realisation der Objekterzeugung entkoppelt und delegiert die Erzeugung der Objekte und den Aufbau des Systems an die Erzeugungsstrukturen.

Beispiel: Abstrakte Fabrik, Erbauer, Singleton

Strukturmuster

Sie zeigen auf, auf welche Art und Weise Klassen bzw. Objekte zu größeren Strukturen zusammengefasst werden können. Die von Strukturmustern beschriebenen Strukturen entstehen zur Laufzeit.

Beispiel: Adapter, Fassade, Proxy

Verhaltensmuster

Sie beschreiben Strukturen, die am Kontrollfluss innerhalb der Anwendung beteiligt sind. Sie konzentrieren sich also auf Algorithmen und die Delegation von Zuständigkeiten.

Beispiel: Beobachter, Besucher, Strategie

Ein erstes konkretes Entwurfsmuster – das Singleton

Die Absicht

Es soll verhindert werden, dass von einem Objekt mehrere Instanzen erzeugt werden können. Dies kann notwendig werden, wenn ein Objekt eine zentral zur Verfügung stehende Ressource nutzen soll, z. B. Druckerwarteschlange, Protokolldatei.

Lösungsidee

Die Klasse, aus der nur ein einziges Objekt erstellt werden soll, verhindert durch ihren Aufbau selbst das Erzeugen mehrerer Objekte. Dazu ist im Folgenden der prinzipielle Aufbau dieser Klasse dargestellt.

Das Klassendiagramm zum Singleton:

classDiagram class Singleton{ -Singleton instance$ -Singleton() +getInstance()$ Singleton }

Im Klassendiagramm stellen die unterstrichenen Elemente sogenannte Klassenelemente dar (Klasseneigenschaft, Klassenmethode). Dazu dient das Schlüsselwort static.

Interfaces

Wird in einer abstrakten Klasse keinerlei Code implementiert, sondern nur nach außen sichtbare Methoden deklariert, so benutzt man in C# dafür ein Interface. Von einem Interface kann keine Instanz erzeugt werden. Das Interface sichert zu, dass Klassen, die es implementieren, die im Interface enthaltenen Methodendeklarationen implementieren.

Im Gegensatz zur Vererbung können Klassen beliebig viele Interfaces implementieren, aber immer nur von einer Oberklasse erben. Dadurch können Objekten gewisse Rollen anhand der Interfacemethoden zugewiesen werden.

In der UML wird ein Interface wie folgt dargestellt:

AWP - Interface

Das Interface Arbeitend kann dabei als vorgegebene Schnittstelle verstanden werden, die zusichert, dass alle Klassen diese Schnittstelle besitzen, die das Interface implementieren. Hier muss die Klasse Schueler und die Klasse Lehrer die Methode arbeite() implementieren, dass die Zusicherung erfüllt wird.

Im C#-Quellcode wird das Interface wie folgt codiert:

public interface Arbeitend
{
    void arbeite();
}

Das Implementieren des Interfaces im Code:

public class Lehrer : Mensch , Arbeitend
{
    ...
    public void arbeite()
    {
        //Implementierung der Methode arbeite() aus dem Interface
    }
}

Gegenüberstellung: Abstrakte Klassen – Interface

  Abstrakte Klassen Interfaces
Methoden konkrete, virtuelle und abstrakte Methoden implizit abstrakte Methoden
Attribute beliebig Keine - In manchen Programmiersprachen als static final möglich
Vererbung eine Klasse kann nur von einer einzigen abstrakten Klasse erben (keine Mehrfachvererbung) eine Klasse kann beliebig viele Interfaces implementieren (und zusätzlich von einer Klasse erben)
Objektbildung keine Objektbildung möglich. Von einer abstrakten Klasse abstammende Unterklasse können Objekte gebildet werden (alle abstrakten Methoden müssen überschrieben werden!) keine Objektbildung möglich. Es können von einer ein Interface implementierenden Klasse Objekte gebildet werden (alle im Interface deklarierten Methoden müssen überschrieben werden!)

Observer

  • Die Absicht: Objekte können Daten bei einem Informationsanbieter abonnieren. Bei jeder Änderung der abonnierten Daten werden die Abonnenten automatisch über die Änderung informiert, die sich dann die geänderten Daten abholen.
  • Das Problem: Voneinander abhängige Objekte (Info-Anbieter und Info-Konsument) sollen nicht zu stark aneinandergekoppelt werden, was die Wiederverwendbarkeit stark einschränken würde.
  • Die Lösungsidee: Trennung von Informationsbereitsteller und einer Menge von Informationsverarbeitern bzw. Darstellern der Information. Die Bereitsteller müssen die Verarbeiter nicht direkt kennen: Keine direkte Kommunikation über Aufrufe, sondern eine indirekte über Benachrichtigungen.

Das Klassendiagramm zum Beobachter:

AWP - Observer 1

Erklärungen zum Observer:

  • Der Info-Anbieter ist das konkrete Subjekt, der Info-Konsument ist der konkrete Beobachter.
  • Es können beliebig viele Observer-Objekte ein Subjekt beobachten, nachdem sie bei ihm registriert wurden (\rightarrow Attach()).
  • Wenn ein Beobachter-Objekt die Beobachtung des Subjektes beenden möchte, muss es sich bei ihm abmelden (\rightarrow Detach()).
  • Der Beobachter stellt eine Schnittstelle (\rightarrow Update()) zur Verfügung, die vom Subjekt aufgerufen werden kann, wenn dessen Zustand sich geändert hat.
    Immer dann, wenn der Zustand des Subjektes geändert wird (\rightarrow setSubjectState()), muss dessen Methode Notify() aufgerufen werden, die dann ihrerseits die Update()-Methode aller registrierten Beobachter aufruft.
  • Der konkrete Beobachter implementiert die Aktualisierungsmethode (\rightarrow Update()) des abstrakten Beobachters, um über Zustands-Änderungen des Subjektes informiert werden zu können.
    Der Beobachter kann dann entweder über eine gespeicherte Subjekt-Referenz den neuen Subjektzustand beim Subjekt abholen (\rightarrow getSubjectState()), oder das Subjekt übergibt der Update()-Methode des Beobachters einen Parameter.

Man kann das Szenario nun leicht um weitere Observer erweitern, die von einem ganz anderen Typ sind, dennoch aber ohne Änderung des restlichen Programmcodes mitverwaltet werden:

AWP - Observer 2

Das Adapter Pattern

TODO - Siehe https://de.wikipedia.org/wiki/Adapter_(Entwurfsmuster)

Das Strategie Pattern

TODO - Siehe https://de.wikipedia.org/wiki/Strategie_(Entwurfsmuster)