Muster: AbstractClassFactory


Die Abstrakte Fabrik (engl. Abstract Factory, Kit) ist ein Erzeugungsmuster (Creational Patterns). Es definiert eine Schnittstelle zur Erzeugung einer Familie von Objekten, wobei die konkreten Klassen der zu instanzierenden Objekte dabei nicht näher festgelegt werden.

Abstrakte Klassenfabrik

Verwendung

Bearbeiten

Die Abstrakte Fabrik findet Anwendung, wenn

  • ein System unabhängig von der Art der Erzeugung seiner Produkte arbeiten soll,
  • ein System mit einer oder mehreren Produktfamilien konfiguriert werden soll,
  • eine Gruppe von Produkten erzeugt und gemeinsam genutzt werden soll oder
  • wenn in einer Klassenbibliothek die Schnittstellen von Produkten ohne deren Implementierung bereitgestellt werden sollen.

Eine typische Anwendung ist die Erstellung einer grafischen Benutzeroberfläche mit unterschiedlichen Themes.

Eine Abstrakte Fabrik vereinigt die Verantwortungen "Zusammenfassung der Objektgenerierung an einer Stelle" und "Möglichkeit zu abstrakten Konstruktoren" (siehe auch unten unter "Verwandte Entwurfsmuster").

Allgemein kann man also sagen:

Das Muster Abstrakte Fabrik findet immer da Anwendung, wo zur Laufzeit entschieden werden soll welche Produktklasse verwendet wird. Denn der Empfänger kann, in solchen Fällen, nicht selbständig die Entscheidung treffen welche Produktklasse er benötigt. Somit liegt die Verantwortung und Entscheidung, welche konkrete Produktlasse benötigt wird, im Bereich der Fabrikklasse.

UML-Diagramm

Bearbeiten

 

  • AbstrakteFabrik
    • definiert eine Schnittstelle zur Erzeugung abstrakter Produkte einer Produktfamilie
  • KonkreteFabrik
    • erzeugt konkrete Produkte einer Produktfamilie durch Implementierung der Schnittstelle
    • ConcreteFactory1 erzeugt die Produkte ConcreteProductA1 und ConcreteProductB1
    • ConcreteFactory2 die Produkte ConcreteProductA2 und ConcreteProductB2.
  • AbstraktesProdukt
    • definiert eine Schnittstelle für eine Produktart
    • Beachte, dass die abstrakten Klassen AbstractProduct1 und AbstractProduct2 in keiner Beziehung zueinander stehen, d.h. sie haben keine gemeinsame Superklasse.
  • KonkretesProdukt
    • definiert ein konkretes Produkt einer Produktart durch Implementierung der Schnittstelle
    • wird durch die korrespondierende konkrete Fabrik erzeugt
  • Klient
    • verwendet die Schnittstellen der abstrakten Fabrik und der abstrakten Produkte

Vorteile

Bearbeiten

Konkrete Klassen werden isoliert

Bearbeiten

Der Client Code wird unabhängig von den konkreten Produkte-Klassen. Er deklariert nur den Interfacenamen oder die abstrakte Produkte-Klasse, jedoch keine konkreten Produkte-Klassen.

Einfaches Auswechseln von Klassen

Bearbeiten

Der Klassenname der konkreten Produkte erscheint genau einmal im Code, nämlich in der ConcreteFactory. Dadurch wird der Austausch durch eine andere Klasse (beispielsweise eine andere Version des Produkts) massiv vereinfacht. Zudem können alle Produkte gemeinsam ersetzt werden, indem eine andere ConcreteFactory Instanz benutzt wird.

Nachteile

Bearbeiten

Unterstützung mit einer neuen Produktefamilie ist aufwändig

Bearbeiten

Um einen neuen Satz von Produkten zu unterstützen ist eine neue ConcreteFactory zu erstellen, und es muss unter sämtlichen AbstractProduct-Klassen eine entsprechende ConcreteProduct-Klasse für die neue Produktefamilie erstellt werden. Bei einer großen Anzahl an Produktearten entsteht dadurch ein erheblicher Aufwand.

Erweiterung durch eine neue Produktart ist schwierig

Bearbeiten

Soll eine neue Produktart hinzugefügt werden (z.B. neben der CPU, MMU noch ein Mainboard), so muss jede Factory mit der entsprechenden Factory Method createMainboard() erweitert werden. Dann muss für das neue Product Mainboard eine abstrakte Klasse AbstractMainboard und für jede Implementation eine konkrete Klasse (gem. Bsp. EmberMainboard, EnginolaMainboard etc.) erstellt werden. Dazu ist das Wissen der Eigenschaften aus allen Produktefamilien notwendig.

Verwendung in der Analyse

Bearbeiten

Wegen der gemeinsamen Komplexität der beiden wesentlichen Verantwortungen ("Zusammenfassung der Objektgenerierung an einer Stelle" und "Möglichkeit zu abstrakten Konstruktoren") ist dieses Pattern für die Analyse praktisch irrelevant.

Klassenhierarchie der Produkte vorbestimmt. Die ConcreteProduct-Klassen sind alle unter ihrer entsprechenden AbstractProduct-Klasse subklassiert. Dadurch können die Produkte keiner weiteren Superklasse angehören, welche aus Client-Sicht vorteilhafter wäre. Durch die Verwendung von Java-Interfaces für die AbstractProducts wird keine Superklasse „verschwendet“. Eine Alternative ist das Design mit dem Bridge Design Pattern

Beispiel

Bearbeiten

Es soll eine Spielesammlung per Software entwickelt werden. Die verwendeten Klassen sind dabei

  1. Spielbrett (erstes abstraktes Produkt), auf das Spielfiguren platziert werden können und das beispielsweise eine Methode besitzt, um sich auf dem Bildschirm anzuzeigen. Konkrete, davon abgeleitete Produkte sind Schachbrett, Mühlebrett, Halmabrett etc.
  2. Spielfigur (zweites abstraktes Produkt), die auf ein Spielbrett gesetzt werden kann. Konkrete, davon abgeleitete Produkte sind Hütchen, Schachfigur (der Einfachheit halber soll es hier nur einen Typ an Schachfiguren geben), Holzsteinchen etc.
  3. Spielfabrik (abstrakte Fabrik), die Komponenten (Spielbrett, Spielfiguren) eines Gesellschaftsspiels erstellt. Konkrete, davon abgeleitete Fabriken sind beispielsweise Mühlefabrik, Damefabrik, Schachfabrik etc.

Ein Klient (z.B. eine Instanz einer Spieler- oder Spielleiter-Klasse) kann sich von der abstrakten Fabrik Spielfiguren bzw. ein Spielbrett erstellen lassen. Je nachdem, welches konkrete Spiel gespielt wird, liefert beispielsweise die...

  • Schachfabrik ein Schachbrett und Schachfiguren
  • Damefabrik ebenfalls ein Schachbrett, aber Holzsteinchen
  • Mühlefabrik ein Mühlebrett, aber ebenfalls Holzsteinchen

Ein Beispiel in Java folgt.

/*
 * GUIFactory example
 */
abstract class Button {
    public abstract void paint();
}

abstract class GUIFactory {
    public static GUIFactory getFactory() {
        int sys = readFromConfigFile("OS_TYPE");
        if (sys == 0) return new WinFactory();
        else return new OSXFactory();
    }

    public abstract Button createButton();
}

class OSXFactory extends GUIFactory {
    public Button createButton() {
        return new OSXButton();
    }
}

class WinFactory extends GUIFactory {
    public Button createButton() {
        return new WinButton();
    }
}
 
class OSXButton extends Button {
    public void paint() {
        System.out.println("I'm an OSXButton: ");
    }
}

class WinButton extends Button {
    public void paint() {
        System.out.println("I'm a WinButton: ");
    }
}

public class Application {
    public static void main(String[] args) {
        GUIFactory factory = GUIFactory.getFactory();
        Button button = factory.createButton();
        button.paint();
    }
    // Output is either:
    //   "I'm a WinButton:"
    // or:
    //   "I'm an OSXButton:"
}

Ein Beispiel in C# ist hier zu finden: http://en.csharp-online.net/Abstract_Factory_design_pattern:_Example

Verwandte Entwurfsmuster

Bearbeiten

Das Muster der Fabrikmethode ist dem der Abstrakten Fabrik strukturell ähnlich, aber sowohl in seiner Motivation als auch in der Verwendung verschieden. Im Gegensatz zur Abstrakten Fabrik zielt dieses Muster nicht auf austauschbare Produktfamilien, sondern auf ein einzelnes Produkt. Dabei wird die Bestimmung der Klasse eines Produktes in die Unterklassen der Erzeugerhierarchie verschoben. Die Erzeugerhierarchie muss dadurch nicht zwangsläufig parallel zur Produkthierarchie sein, wie dies beim Muster der Abstrakten Fabrik der Fall ist.

Möchte man generell eine zusätzliche Hierarchie von Fabriken zu einer Hierarchie von Produkten vermeiden, kann das Muster des Prototyps verwendet werden. Bei diesem Muster werden zur Erzeugung neuer Objekte prototypische Instanzen kopiert.