Das Abstract Factory-Muster

Definition

Das Abstract Factory-Muster (Abstrakte Fabrik) ist eines der GoFEntwurfsmuster (Design Pattern) und gehört zu der Kategorie der Erzeugungsmuster (Creational Design Pattern). Das Muster „bietet eine Schnittstelle zum Erstellen von Familien verwandter oder zusammenhängender Objekte an, ohne konkrete Klassen anzugeben“ (EvKbF S. 156).

Beschreibung

Das Abstract Factory-Muster ist mit dem Factory Method-Muster eng verwandt. Ebenso wie das Factory Method-Muster verwendet das Abstract Factory-Muster eine abstrakte Schnittstelle, um einen Satz verwandter Produkte zu erstellen, ohne die konkreten Produkte zu kennen. Das trägt ebenfalls zur Reduzierung der Abhängigkeiten, da der Client von den konkreten Produkten entkoppelt wird. Auch hier wird das folgende OO-Entwurfsprinzip angewendet:

„Stützen Sie sich auf Abstraktionen. Stützen Sie sich nicht auf konkrete Klassen.“
Das Abhängigkeits-Umkehrungs-Prinzip

Die Methoden in einer Abstrakten Fabrik, werden als Fabrikmethoden implementiert. Jede Methode ist dafür verantwortlich, ein konkretes Produkt zu erstellen. Dazu wird eine Unterklasse der Abstrakten Fabrik erstellt, in der die Implementierung der Methoden erfolgt, die ihrerseits die konkreten Produkte erzeugen. „Fabrikmethoden sind ein natürliches Mittel zur Implementierung von Produkt-Methoden in ihren abstrakten Fabriken.“ (EvKbF S. 158)

Beispiel

Um die Implementierung zu demonstrieren, werden wir das Pizzeria Beispiel des Factory Method-Musters ausbauen. Die abstrakte Pizza Klasse wird um weitere Eigenschaften ergänzt, die die Zutaten für die Pizza abbilden. Weiterhin erhält die Klasse eine weitere Eigenschaft für die abstrakte PizzaIngredientFactory, welche die Schnittstelle zur Erzeugung der Zutaten-Objekte definiert. Die Prepare() Methode wird als abstract deklariert, da dort in den konkreten Pizza Klassen die Zubereitung der Pizza erfolgt.

public abstract class Pizza
{
	public string Name { get; set; }

	public Cheese Cheese { get; set; }

	public Clam Clams { get; set; }

	public Dough Dough { get; set; }

	public Pepperoni Pepperoni { get; set; }

	public Sauce Sauce { get; set; }

	public Veggie[] Veggies { get; set; }

	protected PizzaIngredientFactory IngredientFactory { get; set; }

	protected Pizza(PizzaIngredientFactory ingredientFactory)
	{
		this.IngredientFactory = ingredientFactory;
	}

	abstract public void Prepare();

	public void Bake()
	{
		Console.WriteLine("Bake " + this.Name);
	}

	public void Cut()
	{
		Console.WriteLine("Cut " + this.Name);
	}

	public void Box()
	{
		Console.WriteLine("Box " + this.Name);
	}

	public override string ToString()
	{
		return this.Name;
	}
}

public abstract class PizzaIngredientFactory
{
	public abstract Cheese CreateCheese();
	public abstract Clam CreateClam();
	public abstract Dough CreateDough();
	public abstract Pepperoni CreatePepperoni();
	public abstract Sauce CreateSauce();
	public abstract Veggie[] CreateVeggies();
}

public abstract class Cheese
{ ... }

public abstract class Pepperoni
{ ... }

In der konkreten PizzaIngredientFactory Klasse werden die konkreten Zutaten, in der jeweils dafür vorgesehenen Methode erstellt. Die konkrete Factory wird dann ihrerseits in der konkreten PizzaShop Klasse instanziiert und an die konkreten Pizza Klassen übergeben, die in der Prepare() Methode die Zubereitung der Pizza übernehmen.

public class NewYorkPizzaIngredientFactory : PizzaIngredientFactory
{
	public override Cheese CreateCheese()
	{
		Console.WriteLine("Create Cheese");
		return new ReggianoCheese();
	}

	public override Clam CreateClam()
	{
		Console.WriteLine("Create Clam");
		return new FreshClams();
	}

	public override Dough CreateDough()
	{
		Console.WriteLine("Create Dough");
		return new ThinCrustDough();
	}

	public override Pepperoni CreatePepperoni()
	{
		Console.WriteLine("Create Pepperoni");
		return new SlicedPepperoni();
	}

	public override Sauce CreateSauce()
	{
		Console.WriteLine("Create Sauce");
		return new MarinaraSauce();
	}

	public override Veggie[] CreateVeggies()
	{
		Console.WriteLine("Create Veggies");
		Veggie[] veggies = { new Garlic(), new Onion(), new Mushroom(), new RedPepper() };
		return veggies;
	}
}

public class NewYorkPizzaStore : PizzaStore
{
	protected override Pizza CreatePizza(string type)
	{
		Pizza pizza = null;
		PizzaIngredientFactory ingredientFactory = new NewYorkPizzaIngredientFactory();

		switch (type)
		{
			case PizzaType.CHEESE:
				pizza = new CheesePizza(ingredientFactory);
				pizza.Name = "New York Cheese Pizza";
				break;

			case PizzaType.CLAM:
				pizza = new ClamPizza(ingredientFactory);
				pizza.Name = "New York Clam Pizza";
				break;

			case PizzaType.PEPPERONI:
				pizza = new PepperoniPizza(ingredientFactory);
				pizza.Name = "New York Pepperoni Pizza";
				break;

			case PizzaType.VEGGIE:
				pizza = new VeggiePizza(ingredientFactory);
				pizza.Name = "New York Veggie Pizza";
				break;

			default:
				throw new ArgumentException(String.Format("Unkonown Pizza Type '{0}'", type));
		}

		return pizza;
	}
}

public class CheesePizza : Pizza
{
	public CheesePizza(PizzaIngredientFactory ingredientFactory)
		: base(ingredientFactory)
	{
	}

	public override void Prepare()
	{
		Console.WriteLine("Prepare " + this.Name);

		this.Dough = this.IngredientFactory.CreateDough();
		this.Sauce = this.IngredientFactory.CreateSauce();
		this.Cheese = this.IngredientFactory.CreateCheese();
	}
}

public class PepperoniPizza : Pizza
{
	public PepperoniPizza(PizzaIngredientFactory ingredientFactory)
		: base(ingredientFactory)
	{
	}

	public override void Prepare()
	{
		Console.WriteLine("Prepare " + this.Name);

		this.Dough = this.IngredientFactory.CreateDough();
		this.Sauce = this.IngredientFactory.CreateSauce();
		this.Cheese = this.IngredientFactory.CreateCheese();
		this.Pepperoni = this.IngredientFactory.CreatePepperoni();
	}
}

Fazit

Auch bei diesem Muster wird der Vorteil der Abstraktion deutlich. Durch die Verwendung der PizzaIngredientFactory Schnittstelle, kann eine Pizzeria bei Bedarf die konkrete Factory austauschen, ohne dass Änderungen an anderen Klassen nötig sind. Der Entwurf bleibt damit weiterhin flexibel und Fehler werden vermieden.

UML

Abstract Factory Design Pattern UML Diagram

Akteure

  • AbstractFactory (AbstrakteFabrik)
    Definiert eine Schnittstelle zur Erzeugung abstrakter Produkte einer Produktfamilie.
  • ConcreteFactory (KonkreteFabrik)
    Erzeugt konkrete Produkte einer Produktfamilie durch die Implementierung der Schnittstelle.
  • AbstractProduct (AbstraktesProdukt)
    Definiert eine Schnittstelle für eine Produktart.
  • ConcreteProduct (KonkretesProdukt)
    Repräsentiert ein konkretes Produkt einer Produktart durch die Implementierung der Schnittstelle. Wird durch die korrespondierende konkrete Fabrik erzeugt.
  • Client (Klient)
    Verwendet die Schnittstellen der abstrakten Fabrik und der abstrakten Produkte.

Quellen

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.