Observer Pattern

Die Definition dieses Patterns lautet:

„Definiere eine 1-zu-n-Abhängigkeit zwischen Objekten, so dass die Änderung des
Zustands eines Objekts dazu führt, dass alle abhängigen Objekte benachrichtigt
und automatisch aktualisiert werden.“

Stellen sie sich eine Party vor. Auf dieser Party gibt es 2 Gruppen von Gästen. Die einen die aktiv Erzählen und die anderen die passiv zuhören. Die Erzähler (Observable) interessiert es nicht wer ihnen zuhört, sie schweigen aber wenn sie gar keinen Zuhörer (Observer) haben. Die Zuhörer reagieren auf Witze der Unterhalter.

Realisierung

Um einen Erzähler zu realisieren bietet sich die Klasse Observable an. Diese verfügt über die beiden Methoden setChanged() und notifyObservers(). Mit setChanged() wird die Änderung angekündigt, und mit notifyObservers() wird sie tatsächlich übermittelt.

Würde es keine Änderungen geben, würde notifyObservers auch nichts übertragen.

Wir stellen jetzt unser Party-Szenario nach. Zuerst schreiben wir eine Klasse namens JokeTeller. Die erstellten Objekte erzählen einen Witz und verschicken sie an alle Zuhörer.

class JokeTeller extends Observable{
  private static final List() jokes = Arrays.asList(
    "Sorry, aber du siehst so aus, wie ich mich fühle.",
    "Eine Null kann ein bestehendes Problem verzehnfachen.",
    "Wer zuletzt lacht, hat es nicht eher begriffen.",
    "Wer zuletzt lacht, stirbt wenigstens fröhlich.",
    "Unsere Luft hat einen Vorteil: Man sieht, was man einatmet."
  );

  public void tellJoke(){
    setChanged();
    Collections.shuffle( jokes );
    notifyObservers( jokes.get(0) );
  }
}

setChanged() setzt intern eine Flag. Nach dem Aufruf der notifyObservers() Methode wird diese wieder gelöscht.

Interessierte Zuhörer (Beobachter) müssen sich am Observable-Objekt mit der Methode addObserver anmelden. Gültige Objekte sind aber nur jene, welche die Schnittstelle Observer implementieren. Mit der Methode deleteObserver(Observer) können sie sich wieder abmelden.

Die Namensgebung der Oberklasse ist leider etwas unglücklich gewählt. Normalerweise drückt „able“ immer ein Schnittstelle aus. In diesen Fall ist aber Observable eine echte Klasse während Observer eine Schnittstelle bezeichnet.

Die Observer Schnittstelle

Als nächstes folgt die Realisierung der Zuhörer Klasse. Die hat die Schnittstelle Observer implementiert. Ihre Aufgabe besteht darin auf die Witze des Erzählers zu reagieren. Als Attribute besitzt es nur einen Namen.

class JokeListener implements Observer{
  final private String name;

  JokeListener( String name ){
    this.name = name;
  }

  @Override public void update( Observable o, Object arg ){
    System.out.println( name + " lacht über: \"" + arg + "\"" );
  }
}

Die Party kann beginnen

Um unser Beispiel zu beenden erstellen wir jetzt eine Party Klasse und lassen unser Partygäste miteinander agieren.


public class Party
{
  public static void main( String[] args )
  {
    Observer achim    = new JokeListener( "Achim" );
    Observer michael  = new JokeListener( "Michael" );
    JokeTeller chris  = new JokeTeller();

    chris.addObserver( achim );

    chris.tellJoke();
    chris.tellJoke();

    chris.addObserver( michael );

    chris.tellJoke();

    chris.deleteObserver( achim );

    chris.tellJoke();
  }
}

Anwendung

Das Observer Pattern findet sich in fast allen Grafischen Oberflächen wieder. In der Java Welt werden sie als Listener bezeichnet. Besten Beispiel ist ein einfacher Button.

Von Natur aus ist nicht definiert was passieren soll wenn dieser gedrückt wird. Ein EventListener wird an den Button angehängt, der reagieren soll sobald ein bestimmtes Ereignis eintritt. In unseren einfachen Fall wenn der Button betätigt wird.

Zusammenfassung

  • Observer findet immer dann Anwendung, wenn sich der Status eines Objekts ändert und beliebig viele weitere Objeke darüber informiert werden
  • Es gibt Ereignisquellen und Beobachter
  • Beobachter müssen austauschbar sein

Template Method Pattern

Die Definition dieses Templates lautet:

„Definiere das Skelett eines Algorithmus in einer Operation und delegiere einzelne Schritte an Unterklassen. Die Verwendung einer Schablonenmethode ermöglicht es Unterklassen, bestimmte Schritte eines Algorithmus zu überschreiben, ohne seine Struktur zu verändern.“

Das Template Method Pattern oder auch auf Deutsch Schablonenmethode gehört zu Kategorie der Behavioral Design Patterns. In der abstrakten Parent Klasse wird die Struktur der Methoden definiert. Die genaue Funktionsweise wird aber erst in den dazugehörigen Unterklassen definiert.

Realisierung

Ein einfaches Beispiel lautet wie folgt, ein Programm liest einen eingegeben Text und modifiziert ihn. Eine Methode gibt den Text nur in Großbuchstaben aus während im anderen Fall nur Kleinbuchstaben zurückgegeben werden.

Wir erstellen eine abstrakte Klasse mit dem Namen Eingabe. Die abstrakte Klasse ist nur für das einlesen und auslesen des Textes verantwortlich. Das bearbeiten übernimmt eine Unterklasse.


public abstract class Eingabe{
	public final void run(){
		String eingabe = textEingeben();
		String konvertiert = convert(eingabe);
		drucke(konvertiert);
	}
	private final String textEingeben(){
		final String MESSAGE = "Bitte geben Sie den Text ein: ";
		return JOptionPane.showInputDialog(MESSAGE);
	}	
	protected abstract String convert(String eingabe);
	private final void drucke(String text)
	{
		System.out.println(text);
	}
}

Jetzt werden 2 Unterklassen definiert, wo deklariert ist  auf welche Art und Weise der Text bearbeitet wird. Die Klasse UppercaseConverter wandelt den Text in Großbuchstaben um, während die Klasse LowercaseConverter den Text in Kleinbuchstaben ändert.

Die beiden Klassen unterscheiden sich nur minimal:

LowercaseConverter:

public class LowercaseConverter extends Eingabe{
	@Override
	protected String convert(String eingabe){
		return eingabe.toLowerCase();
	}
}

UppercaseConverter:

public class UppercaseConverter extends Eingabe{
	@Override
	protected String convert(String eingabe){
		return eingabe.toUpperCase();
	}
}

In der Oberklasse ist der Algorithmus definiert und die Oberklasse ruft die entsprechende Methode in der Unterklasse auf. Dieses Verhalten wird auch als das Hollywood-Prinzip bezeichnet.

Hollywood-Prinzip

Rufen Sie uns nicht an – wir rufen Sie an!

Dank des Hollywood Prinzipes kann der Client leicht den gewünschten Converter auswählen:

public class Client{
	public static void main(String[] args){
		Eingabe eingabe = new LowercaseConverter();
		eingabe.run();
		Eingabe neueEingabe = new UppercaseConverter();
		neueEingabe.run();
	}
}	

Zusammenfassung

  • Definition einen Algorithmus
  • Teile des Algorithmus werden von der Oberklasse selbst ausgeführt, andere Teile werden von Subklassen vorgeschrieben, die die nicht abstrakten Teile implementieren
  • Den Algorithmus beschreiben Sie in einer finalen Methode der abstrakten Oberklasse

Was sind Design Patterns?

Die Definition von Design Patterns lautet:

Design Patterns (Entwurfsmuster) sind bewährte Muster oder Lösungen für Probleme, die immer wieder auf eine bestimmte Art auftreten. Sie lassen sich genauso wenig erfinden wie die Zahl Pi – sie werden entdeckt.

Fast alle Probleme die während des Programmierens auftreten wurden schon in irgend einer Art und Weise schon einmal gelöst. Die früheren Lösungen können auf das jetzige Problem umgelegt werden. Die Lösung ist sogar so Grundlegend das es auch viele Lösungen der Gleichen Art umlegen lässt.

Wenn die Problemlösung von vielen Entwicklern verwendet wird spricht man von einem Design Pattern oder auf Deutsch Entwurfsmuster.

Im 1995 verfassten Erich Gamma, Richard Helm, Ralph Johnson und John Vlissides das Buch „Design Patterns elements of Reusable Object-Oriented Software„. Diese 4 Herren auch öfters „Gang of Four“ (GOF) genannt haben in Ihren Buch das grundlegende Wissen und Erfahrung von Softwareentwickler zusammengetragen. Das dort Verfasste ist so grundlegend das ihr Werk fast unverändert gedruckt wird.

Design Patterns werden in 3 Kategorien unterteilt:

  1. Erzeugungsmuster
  2. Verhaltensmuster
  3. Strukturmuster

 

Strukturmuster

Strukturmuster oder auch Structural Design Patterns beschreiben wie Klassen zusammengesetzt werden, um größere Einheiten zu bilden. Die einfachste Art um sich ein Strukturmuster vorstellen ist der Adapter. Ein klassischer Adapter außerhalb der Coding Welt kennen wir von Steckdosen.

Im Ausland benötigen wir einen Adapter, damit unser Geräte an die dortigen Steckdosen angesteckt werden können.

Beispiel für Strukturmuster sind:

  • Adapter
  • Facade Pattern
  • Proxy

Verhaltensmuster

Das Verhaltensmuster oder auch Behavioral Design Patterns genannt, beschreibt wie Klassen und Objekte zusammenarbeiten. Ein gutes Beispiel für ein Verhaltensmuster ist das Observer Pattern

Ein Objekt ändert seinen Zustand und ein anderes Objekt möchte darüber informiert werden. Wer informiert wenn? Fragt das eine Objekt nach oder wird es informiert? Gilt eine Hol oder eine Bring Schuld?

Die Aufgabe von Verhaltensmuster bestehen darin Vorschläge zu machen welches Objekte welche Aufgabe übernimmt.

Beispiele für Verhaltensmuster