Design Patterns – Proxy

Das Proxy-Pattern legt eine Zugriffskontrollschicht über ein Objekt (Subjekt). Da Originalobjekt wie Proxyobjekt das selbe Interface Subject implementieren, kann das Proxyobjekt als Stellvertreter (Proxy) verwendet werden.
So alltäglich der Zugriff auf Objekte für den Programmierer ist, so mannigfaltig können die Zugriffskontrollen des Proxy sein. Der Proxy kann etwa den Zugriff auf ein entferntes Objekt kapseln und kann für den Client unsichtbar das Verbindungsmanagement übernehmen (Remote Proxy). Ohne es zu wissen, habe ich diese Art Proxy verwendet, um den Aktienwerte zurück gebenden Webservice zu testen. Anhand der WSDL generiert mir Visual Studio eine Servicereferenz, die als Proxy dient.
Das Proxy kann auch zu dem Zweck existieren, das gekapselte Objekt erst bei Bedarf zu instanziieren (Virtual Proxy). Im GoF-Buch gibt es z.B. ein Dokument-Objekt, welches eine Vielzahl von primitiven Objekten wie Texte, geomentrische Elemente und Bilder enthält. Würde jedes Bildobjekt sofort instanziiert werden, könnte das sehr lange dauern und wäre in vielen Fällen unnötig.
Ein Schutzproxy erlaubt nur autorisierte Zugriffe auf das Objekt. Hier kann der Objektzugang benutzer- oder gruppenspezifisch geregelt werden.
Im Unterschied zum Decorator fügt der Proxy dem Subjekt keine Funktionalität hinzu. Es wird lediglich der Zugriff gesteuert.

Design Patterns – State

Das Zustand-Muster erlaubt es einem Objekt zur Laufzeit zustandsabhängig sein Verhalten zu ändern. Im Zeitalter der Objektorientierung programmiert man nach diesem Muster Zustandsautomaten. Statt für jede Zustandswechselmethode eine umfangreiche if-else-Orgie zu veranstalten, wird jeder Zustand in eine Klasse gekapselt. Zustände können so wesentlich leichter hinzugefügt werden, ohne jedesmal die if-else-Anweisungen erneut verstehen zu müssen. Selbst bei einem sehr komplexen Zustandsautomaten bleibt die Wartung übersichtlich.

<?php
abstract class WindowState 
{
	public function OpenWide($window)
	{
	    echo "Keine Zustandsänderung<br/>";
	}
	public function Close($window) 
	{
	    echo "Keine Zustandsänderung<br/>";
	}
	public function Tilt($window) 
	{
	    echo "Keine Zustandsänderung<br/>";
	}
}

class WindowClosedState extends WindowState 
{
	public function OpenWide(Window $window) 
	{
	    echo "Fenster wurde geöffnet<br/>";
	    $window->currentState=new WindowWideOpenedState();
	}
	public function Tilt(Window $window) 
	{
	    echo "Fenster wurde gekippt<br/>";
	    $window->currentState=new WindowTiltState();
	}
}

class WindowWideOpenedState extends WindowState 
{
	public function Tilt(Window $window) 
	{
	    echo "Fenster wurde gekippt<br/>";
	    $window->currentState=new WindowTiltState();
	}
	public function Close(Window $window) 
	{
	    echo "Fenster wurde geschlossen<br/>";
	    $window->currentState=new WindowClosedState();
	}
}

class WindowTiltState extends WindowState 
{
	public function Close(Window $window) 
	{
	    echo "Fenster wurde geschlossen<br/>";
	    $window->currentState=new WindowClosedState();
	}
	public function Open(Window $window) 
	{
	    echo "Fenster wurde geöffnet<br/>";
	    $window->currentState=new WindowOpenedState();
	}
}
class Window
{
	public $currentState;

	public function Window()
	{
		$this->currentState=new WindowClosedState();
	}

	public function OpenWide()
	{
		$this->currentState->OpenWide($this);
	}

	public function Close()
	{
		$this->currentState->Close($this);
	}

	public function Tilt()
	{
		$this->currentState->Tilt($this);
	}
}

$window = new Window();
$window->Close();

$window->OpenWide();
$window->Tilt();
$window->Tilt();
$window->OpenWide();
$window->Close();

?>

Design Patterns – Composite

Bei einer Komposition (HAS-A-Beziehung von Container/Komponenten) mag es vorkommen, dass Komponente wie Container gleich behandelt werden sollen. Ein GUI-Element kann beispielsweise ein Container oder ein primitives Element wie ein Control sein. Beide Arten, Container wie Control haben aber viele gleiche Eigenschaften und Methoden, wie Draw(), Size(), Move(x,y), usw. Für eine flexible Verwendung leitet man Container und Komponente vom selben Interface ab. Sie verhalten sich damit nach außen gleich. Wenn nun z.B. Draw() auf dem Container aufgerufen wird, braucht dieser nur sich selbst zu zeichnen und danach auf seinen Komponenten Draw() aufzurufen, welche ihrerseits wieder beides sein können.

Hier ein Codeschnipsel in PHP:

<?php

interface IComponent
{
    public function GetSize();
    public function GetWeight();
}

class Component implements IComponent
{
	private $width;
	private $height;
	private $length;
	private $weight;

	public function __construct($width, $height, $length, $weight)
    {
        $this->width = $width;
        $this->height = $height;
        $this->length = $length;
        $this->weight = $weight;
    }

    public function GetSize()
    {
    	return $this->width * $this->height * $this->length;
    }
    
    public function GetWeight()
    {
    	return $this->weight;
    }
}

class Container extends Component implements IComponent
{
    private $components = array();
    public function __construct($width, $height, $length, $weight)
    {
        parent::__construct($width, $height, $length, $weight);
    }

    public function Add ($component)
    {
    	$this->components[] = $component;
    }
    
    public function GetSize()
    {
    	return parent::GetSize();
    }
    
    public function GetWeight()
    {
    	$weight = parent::GetWeight();
        foreach ($this->components as $info => $value )
        	$weight += $value->GetWeight();
		
        return $weight;
   	}
}

    $container1 = new Container(10,10,10,1000);
    $component2 = new Component(2,2,2,200);
    $container = new Container(100, 100, 100, 100000);
    $container->Add($container1);
    $container->Add($component2);
    
    $minicontainer = new Container (8,8,8,8);
	$minicomponent = new Component (2,2,2,2);    
	$minicomponent2 = new Component (2,2,2,2);
	$minicontainer->Add($minicomponent);
	$minicontainer->Add($minicomponent2);
	$container1->Add($minicontainer);  
	  
	echo "Total Weight: <br\>";
   	echo $container->GetWeight();
 
?>

Mein Beispiel stellt eine Schiffscontainerstruktur dar. Container wie Component sind beide vom Typ IComponent und bieten die Methoden GetSize() und GetWeight() an. Statt bei der Gewichtsberechnung den Typ zu unterscheiden brauche ich nur GetWeight() aufzurufen.

Design Patterns – Iterator

Mit Hilfe des Iterator Patterns kann sequenziell auf die Elemente eines zusammengesetzten Objekts (Aggregat) zugegriffen werden, ohne die Struktur der Elemente offen zu legen. Ein Iterator erlaubt diesen Zugriff.
Es wird verwendet, wenn mehrere Iteratoren gleichzeitig ermöglicht werden sollen, wenn ein Iterator polymorph verwendet werden soll oder wenn Datenstrukturen sich ändern aber man kein Clientcode ändern möchte. Außerdem kann ein Iteratorverhalten zur Laufzeit ausgetauscht werden. Das Muster beherzigt das Single Responsibility Principle. Eine Klasse soll nur eine Verantwortlichkeit haben.

Hier ein Codeschnipsel in PHP:

<?php

class Person
{
	private $name = "";
    public function __construct($n)
    {
        $this->name = $n;
    }

    public function Name()
    {
    	return $this->name;
    } 
}

class NameFilterIterator extends FilterIterator
{
    private $nameFilter;
   
    public function __construct(Iterator $iterator , $filter )
    {
        parent::__construct($iterator);
        $this->nameFilter = $filter;
    }
   
    public function accept()
    {
        $person = $this->getInnerIterator()->current();
        if( strcasecmp($person->Name(),$this->nameFilter) == 0) {
            return false;
        }       
        return true;
    }
}

class ConcreteAggregateArray implements IteratorAggregate
{
    protected $_array = array();
	public function __construct()
    {
    	$this->_array[] = new Person("Hans");
    	$this->_array[] = new Person("Seppel");
        $this->_array[] = new Person("Dirk");
   	}
        
    public function getIterator()
    {
        return new ArrayIterator($this->_array);
    }
}

class ConcreteAggregateObjectStore implements IteratorAggregate
{
	protected $objectStore;

	public function __construct()
    {
    	$this->objectStore = new SplObjectStorage();
    	$this->objectStore->attach(new Person("Lilli"));
    	$this->objectStore->attach(new Person("Eva"));
        $this->objectStore->attach(new Person("Herta"));
    }
        
    public function getIterator()
    {
        return $this->objectStore;
    }
}

class ConcreteAggregateFilteredArray implements IteratorAggregate
{
    protected $_array = array();
	public function __construct()
    {
    	$this->_array[] = new Person("Albert");
    	$this->_array[] = new Person("Adelheid");
        $this->_array[] = new Person("Anton");
   	}
        
    public function getIterator()
    {
        return new NameFilterIterator(new ArrayIterator($this->_array), "Adelheid");
    }
}

function PrintPersons($iteratorAggregate)
{
    foreach ($iteratorAggregate->getIterator() as $info => $value ) 
    {
        echo $value->Name();
        echo "<br />";
    }	
}

$aggregates = array(new ConcreteAggregateArray, 
					new ConcreteAggregateObjectStore, 
					new ConcreteAggregateFilteredArray);
foreach ($aggregates as $info => $value)
	PrintPersons($value)	
?>

Die Aggregate ConcreteAggregateArray, ConcreteAggregateObjectStore und ConcreteAggregateObjectStore implementieren das IteratorAggregate-Interface und geben jeweils einen konkreten Iterator, welcher von Iterator abgeleitet ist, zurück. Dadurch kann das Aggregat polymorph verwendet werden. Das beweist die Funktion function PrintPersons($iteratorAggregate). Mittels foreach kann ungeachtet der konkreten Form über das Aggregat iteriert werden.

Design Patterns – Template Method

Die Schablonenmethode (Template Method) definiert das Skelett eines Algorithmus und delegiert einzelne Schritte an Unterklassen weiter. Dadurch können Unterklassen bestimmte Schritte eines Algorithmus bestimmen, ohne die Struktur des Algorithmus zu verändern. Hier wird das Designprinzip “Don’t call us, we call you” verwirklicht.
Die Schablonenmethode sollte angewendet werden, um invariante Anteile eines Algorithmus genau einmal zu definieren und variierende Anteile an Unterklassen zu übergeben. Die Templatemethode ist eine häufig verwendete Refaktorisierngsmethode. Gemeinsame Teile werden aus Basisklassen in eine Templatemethode herausgezogen. Dadurch wird sichergestellt, dass doppelter Code vermieden wird. Durch Verwendung einer Schablonenmethode werden Unterklassen kontrolliert erweitert. Einschubmethoden, die in der abstrakten Klasse häufig leer implementiert sind, können in Unterklassen überschrieben werden. Die Verwendung der Einschubmethoden ist allerdings in der abstrakten Klasse geregelt.

Hier als Beispiel ein PHP-Schnipsel

<?php

abstract class KindergartenTrip
{
	protected function __construct($count)
	{
  		$this->memberCount = $count;
  	}

  	abstract protected function DoAntemeridianTrip();
  	abstract protected function DoPostmeridianTrip();

	private $memberCount = 0;
	private $collectedMembersCount = 0;

	public final function doTrip()
  	{
  		if ($this->memberCount == 0)
  		return;

  		if ($this->collectedMembersCount != $this->memberCount)
  			return;

  		$this->TravelToDestination();
  		$this->DoAntemeridianTrip();
  		$this->DoPostmeridianTrip();
		$this->TravelHome();

  	}

  	public final function SetCollectedChildren($count)
  	{
		$this->collectedMembersCount = $count;
  	}

  	public function TravelToDestination()
  	{
  		echo "Normal Travel to Destination<BR>";
  	}
  	public function TravelHome()
  	{
  		echo "Normal Hometravel<BR>";
  	}

}
class SchwimmbadTrip extends KindergartenTrip
{
	public function __construct($count)
	{
		parent::__construct($count);
  	}

  	public function DoAntemeridianTrip()
  	{
  		echo "Go swimming<BR>";
  	}

  	public function DoPostmeridianTrip()
  	{
  		echo "Go swimming once again<BR>";
  	}

  	public function TravelHome()
  	{
  		echo "Few children were sick<BR>";
  	}
}

class SpecialFunTrip extends KindergartenTrip
{
	public function __construct($count)
	{
		parent::__construct($count);
  	}

  	public function DoAntemeridianTrip()
  	{
  		echo "Go to circus<BR>";
  	}

  	public function DoPostmeridianTrip()
  	{
  		echo "Go to cinema<BR>";
  	}

  	// Einschubmethode
  	public function TravelToDestination()
  	{
  		echo "Children very excited<BR>";
  	}

  	// Einschubmethode
  	public function TravelHome()
  	{
  		echo "Children very happy an exhausted<BR>";
  	}
}
?>

Die abstrakte Klasse ist KindergartenTrip und die Templatemethode doTrip(). doTrip() ist als public final definiert, da die Methode nicht mehr überschrieben werden soll. Für alle Trips gilt die Invariante, dass die Anzahl der eingesammelten Kinder gleich der Anzahl der angemeldeten Kinder sein muss. Die öffentliche Methode SetCollectedChildren($count) soll ebenfalls in abgeleiteten Klassen nicht geändert werden können. Die Templatemethode legt als Invariante fest, dass der Trip erst losgeht, wenn die Anzahl der aufgelesenen Kinder gleich der Anzahl der angemeldeten Kinder ist. Außerdem wird die Reihenfolge der Schritte des Tripalgorithmus festgelegt. TravelToDestination() und TravelHome() sind Einschubmethoden, die überschrieben werden können. Konkrete Klassen müssen die variablen Schritte DoAntemeridianTrip() und DoPostmeridianTrip() implementieren.

Design Patterns – Facade

Die Fassade (Facade) dient dazu, ein einheitliches Interface zu einer Menge von Interfaces eines Subsystems bereit zu stellen. Dadurch wird die Komplexität aus dem Subsystem herausgenommen und die Benutzung vereinfacht. Die Interfaces des Subystems können auch weiter seperat genutzt werden. Die Fassade ist das wohl häufigste Entwurfsmuster, mindestens was die Benutzung betrifft. Wie oft habe ich mich dabei ertappt, wie ich stundenlang nach dem einfachsten Weg suche. Hier gehts ums nackte Überleben, denn complexity kills.
Dennoch einige Beispiele:

  • Die MFC ist Microsofts Fassade um die komplizierte Windows-API, um die Erstellung von Windows-Anwendungen zu vereinfachen.
  • In Java sind viele manuelle Schritte nötig, nur um eine Messagebox mit den Low-Level-Klassen von Swing auf den Bildschirm zu werfen. Mit der Fassade JOptionPane.showMessageDialog(...) kann man sich viele Zeilen sparen und dies ist schnell erledigt.
  • Java Swing wiederum ist selbst eine Fassade, um nicht jede geometrische Figur einzeln zeichnen zu müssen.
  • Eine Computersprache selbst ist eine Fassade auf die nächst niedrigere Ebene. C#-Code ruft intern C-Code auf und dieser Assembler. Der Benutzer selbst hat die abstrakteste Fassade vor sich.

Die Beispiele sind wohl unendlich. Ob es zu den wirklichen Fortschritten gehört, eine Fassade etwa um eine STL-Collection zu bauen, die die Methoden Count() und GetAt(int) anbietet, nur weil man die die letzten 20 Jahre so programmiert hat, darüber lässt sich streiten.

Design Patterns – Adapter

Das Adapter Pattern dient dazu, eine Klasse (Adaptee) wieder zu verwenden, indem man sie mittels eines Adapters zu einem von einer Anwendung geforderten Interface (Target) kompatibel macht. Dabei hat der Adapter in der objektorientierten Programmierung genau die selbe Bedeutung wie der Adapter im Alltag. Z.B. passt mein deutscher Rasierer prima in eine deutsche Steckdose. Genauso prima passt ein amerikanischer Rasierer in eine amerikanische Steckdose. Beide Steckdosen haben ihre absolute Daseinsberechtigung. Ich möchte für meinen deutschen Rasierer nicht die amerikanische Steckdose aus der Wand reißen, sondern ich passe einfach meinen Rasierer mittels eines Adpters an. Übertragen auf die Softwareentwicklung bedeutet es, dass ich eine bestimmte Klasse wieder verwenden möchte. Ich möchte oder kann die Implementierung der Klasse aber nicht ändern. Z.B. fehlt mir bei einer gekauften Bibliothek der Quellcode. Es ist aber sehr oft auch gar nicht erstrebenswert die für eine allgemeine Bibliothek verfasste Klasse für jeden Anwendungsfall zu spezialisieren. Ich will die existierende Klasse aber auch nicht neu implementieren. Das können andere besser. Oder meine bestehenden Klassen, die das Zielinterface implementieren und die wieder zu verwendende Klasse sind mir schon genug des Guten. Für diese Fälle benutze ich einen Adapter.

Beispiel in C#:


using System;
using System.Diagnostics;

namespace AdapterPattern
{
    class Adaptee
    {
        public int CalculateCaloriesPerDay()
        {
            // Complicated Legacy Calculation
            return 50;
        }
    }

    interface ITarget
    {
        int GetKCalPerDay();
    }

    class ClassAdapter : Adaptee, ITarget
    {
        public int GetKCalPerDay()
        {
            return CalculateCaloriesPerDay();
        }
    }

    class ObjectAdapter : ITarget
    {
        public ObjectAdapter(Adaptee adaptee)
        {
            _adaptee = adaptee;
        }
        private Adaptee _adaptee;

        public int GetKCalPerDay()
        {
            if (_adaptee == null)
                return -1;

            return _adaptee.CalculateCaloriesPerDay();
        }
    }

    class Client
    {
        static void Main()
        {
            Adaptee caloriesCalculator = new Adaptee();
            ITarget cAdapter = new ClassAdapter();
            ITarget oAdapter = new ObjectAdapter(caloriesCalculator);

            Debug.Assert(cAdapter.GetKCalPerDay() == oAdapter.GetKCalPerDay());

        }
    }
}

Der Klassenadapter wird von Target und Adaptee abgeleitet. Dadurch adaptiert er genau nur den Adaptee. Zumindest C# unterstützt keine direkte Mehrfachvererbung. Die Adaptierung erfolgt zur zur Kompilierzeit. Die Adaptierung ist aber einfacher als beim Objektadapter. Bei letzterem bestücke ich meinen Adapter per Komposition mit dem Adaptee. Das kann zur Laufzeit geschehen und ich kann mehrere Adaptees adaptieren.

Design Patterns – Command

Das Command-Pattern dient dazu, einen Aufruf in einem Objekt zu verpacken. Aufrufer können so zur Laufzeit mit Aufrufen bestückt werden. Aufrufe können wieder verwendet und in Listen gehalten werden. Dadurch können Aufrufe zu Makros kombiniert oder nacheinander rückgängig (Undo) gemacht werden. Aufrufer werden von aufgerufenem Empfänger strikt getrennt.

Beispielcode (C#):

using System;
using System.Collections;

namespace CommandPattern
{
    public class Player
    {
        private int _xpos;
        public int XPos
        {
            get { return _xpos; }
            set
            {
                _xpos = value;
                Console.WriteLine("XPos = {0}, YPos = {1}", _xpos, _ypos);
            }
        }

        private int _ypos;
        public int YPos
        {
            get { return _ypos;  }
            set
            {
                _ypos = value;
                Console.WriteLine("XPos = {0}, YPos = {1}", _xpos, _ypos);
            }
        }
    }

    public interface ICommand
    {
        void Execute();
        void Undo();
    }

    public class CommandMoveUp : ICommand
    {
        private Player _player;
        public CommandMoveUp(Player player)
        {
            _player = player;
        }
        public void Execute()
        {
            _player.YPos += 1;
        }
        public void Undo()
        {
            _player.YPos -= 1;
        }
    }

    public class CommandMoveDown : ICommand
    {
        private Player _player;
        public CommandMoveDown(Player player)
        {
            _player = player;
        }
        public void Execute()
        {
            _player.YPos -= 1;
        }
        public void Undo()
        {
            _player.YPos += 1;
        }
    }

    public class CommandMoveLeft : ICommand
    {
        private Player _player;
        public CommandMoveLeft(Player player)
        {
            _player = player;
        }
        public void Execute()
        {
            _player.XPos -= 1;
        }
        public void Undo()
        {
            _player.XPos += 1;
        }
    }

    public class CommandMoveRight : ICommand
    {
        private Player _player;
        public CommandMoveRight(Player player)
        {
            _player = player;
        }
        public void Execute()
        {
            _player.XPos += 1;
        }
        public void Undo()
        {
            _player.XPos -= 1;
        }
    }

    public class MoveControl
    {
        public CommandMoveDown MoveDownCommand { get; set; }
        public CommandMoveUp MoveUpCommand { get; set; }
        public CommandMoveLeft MoveLeftCommand { get; set; }
        public CommandMoveRight MoveRightCommand { get; set; }

        private Stack _commandStack = new Stack();

        public void MoveUp()
        {
            MoveUpCommand.Execute();
            _commandStack.Push(MoveUpCommand);
        }
        public void MoveDown()
        {
            MoveDownCommand.Execute();
            _commandStack.Push(MoveDownCommand);
        }
        public void MoveLeft()
        {
            MoveLeftCommand.Execute();
            _commandStack.Push(MoveLeftCommand);
        }
        public void MoveRight()
        {
            MoveRightCommand.Execute();
            _commandStack.Push(MoveRightCommand);
        }
        public void Undo()
        {
            if (_commandStack.Count == 0)
                return;

            Console.WriteLine("Undo:");
            var command = (ICommand)_commandStack.Pop();
            if (command != null)
                command.Undo();
        }
    }

    class Client
    {
        static void Main(string[] args)
        {
            var receiver = new Player();
            var moveUpCommand = new CommandMoveUp(receiver);
            var moveDownCommand = new CommandMoveDown(receiver);
            var moveLeftCommand = new CommandMoveLeft(receiver);
            var moveRightCommand = new CommandMoveRight(receiver);

            KeyboardControl invoker = new KeyboardControl();
            invoker.MoveUpCommand = moveUpCommand;
            invoker.MoveDownCommand = moveDownCommand;
            invoker.MoveLeftCommand = moveLeftCommand;
            invoker.MoveRightCommand = moveRightCommand;

            while (true)
            {
                System.ConsoleKeyInfo KInfo = Console.ReadKey(true);
                switch (KInfo.Key)
                {
                    case ConsoleKey.UpArrow:
                        invoker.MoveUp();
                        break;
                    case ConsoleKey.DownArrow:
                        invoker.MoveDown();
                        break;
                    case ConsoleKey.LeftArrow:
                        invoker.MoveLeft();
                        break;
                    case ConsoleKey.RightArrow:
                        invoker.MoveRight();
                        break;

                    case ConsoleKey.Spacebar:
                        invoker.Undo();
                        break;

                }
            }
        }
    }
}

Das Bild zeigt, dass Undo verlässlich funktioniert:
Undo

Der längliche Code spiegelt eine Eigenheit des Musters nieder. Da Befehle so eingekapselt werden, dass sie nur noch über die Methoden Execute() und Undo() verfügen, gibt es entsprechend viele Befehlsklassen. Im Beispiel erzeugt Client 4 Befehle, um eine Spielfigur jeweils 1 Schritt nach oben, unten, rechts und links gehen zu lassen. Den Befehlen wird ein ebenso erzeugter Receiver (Player) verpasst. Mit diesen Befehlen wird ein Invoker (MoveControl) bestückt (parametrisiert). MoveControl merkt sich alle aufgerufenen Befehle in einem Stack, so das auf Undo-Wunsch Undo() auf den Befehlen aufgerufen werden und diese wieder vom Stack genommen werden können.
In diesem Beispiel ist die Anzahl der Befehle noch überschaubar. Man kann sich leicht komplexere Beispiele, wie einen Rechner vorstellen, der über endlose Befehle verfügen kann wie Rechne5mal5, Rechne5mal6, Rechne1plus2, … Hier kommt man nicht umhin, für jede Anfrage des Benutzers zur Laufzeit einen neuen Befehl zu erzeugen, z.B. new CalculateCommand (string operator, double operand) und dem Invoker unterzujubeln, bevor man diesen den Befehl aufrufen lässt. Nur so können Invoker und Receiver getrennt bleiben.

Design Patterns – Singleton

Das Singleton-Objekt existiert nur einmal. Das wird dadurch sicher gestellt dass der Konstruktor der Singletonklasse privat ist. Um eine Instanz zu erzeugen, existiert eine statische Elementfunktion, die einer Instanzvariablen, falls diese uninitialisiert ist, eine Instanz von Singleton zuweist und diese zurück gibt. Ein Objekt wird also nur erzeugt, wenn es benötigt wird und das genau einmal.
Wird diese Klasse von verschiedenen Threads benutzt, gibt es zusätzlich einiges zu beachten, welches ich dann bei Bedarf nachlesen werde.
Ein großer Nachteil beim Singleton besteht darin, dass er die von der Objektorientierung favorisierte Kapselung für seine Einzigartigkeit opfert. Ein Singleton ist global verfügbar und jeder kann das Objekt manipulieren, falls es über öffentliche Setter verfügt. Hier muss man wie bei allem Vorteile und Nachteile abwägen. Man sollte den Singleton also nur einsetzen, wenn das Bedürfnis überwiegt, sicher zu stellen, dass genau eine Instanz des Singleton existiert.

Design Patterns – Factory Method

Die abstrakte Fabrik hatte ich ja schon. Aber was ist der Unterschied zur Fabrikmethode? Ich zitiere aus Objektorientierte Programmierung – Das umfassende Handbuch:

  • Die Fabrikmethode existiert nicht an einer eigens dafür erstellten Klasse, sondern sie ist eine zusätzliche Methode in einer bereits existierenden Klassenhierarchie.
  • Die erzeugenden Klassen haben in der Regel eine konkrete technische oder fachliche Aufgabenstellung. Im Rahmen dieser Aufgabenstellung benötigen sie Exemplare von Klassen, die in einer dazu korrespondierenden parallelen Hierarchie aufgebaut sind.
  • Die konkreten abgeleiteten Klassen der Erzeuger nutzen nun die Fabrikmethode, um Exemplare der jeweils benötigten konkreten Produktklassen zu erstellen.

Auch abstrakte Fabriken nutzen in der Regel Fabrikmethoden, um ihre Produkte zu erzeugen. Der entscheidende Unterschied ist aber: Die Klassenhierarchie, die für die abstrakte Fabrik aufgebaut wird, hat ausschließlich den Zweck, diese Produkte zu erzeugen.

Beispiel: Die Fabrikmethode GetEnumerator() gehört dem IEnumerable Interface an und liefert einen IEnumerator. Die das IEnumerable implementierende Klassenhierarchie, zu der z.B. ArrayList, Dictionary oder List gehören, hat die Aufgabe, mit Collections umzugehen. Dazu ist ein Iterator unerlässlich. In Patternsprache bedeutet das: Eine konkrete Fabrik z.B. ArrayList gibt per GetEnumerator() einen konkreten Iterator ArrayListEnumeratorSimple zurück, welcher vom Klienten über das IEnumerator Interface benutzt wird.