2011. január 14., péntek

Elvont Gyár (Abstract Factory)

A Gyár (Factory) célja, hogy objektumokat gyártson úgy, hogy a gyártás részleteivel a hívónak nem kell foglalkoznia. Az Elvont gyár (Abstract Factory) pedig egy felületet biztosít a gyárak számára a konkrét osztályok megadása nélkül, biztosítva így a továbbfejlesztési lehetőséget.

Alkalmazhatóság

Akkor használjuk az Abstract Factory mintát, ha
  • A rendszernek függetlennek kell lenni attól, hogyan hozza létre, állítja össze és jeleníti meg a termékeit.
  • A rendszernek be kell állítania egyet a termékcsalád közül.
  • Kapcsolódó termékobjektumok egy családját együttes használatra tervezték, és ezt a megszorítást ki kell kényszeríteni.
  • Gondoskodni kell a termékek osztálykönyvtáráról, és csak az interfészüket fedhetjük fel, de a megvalósításukat nem.
Struktúra


Résztvevők
  • AbstractFactory: az absztrakt termékobjektumokat létrehozó interfészt deklarálja.
  • ConcreteFactory: a konkrét termékobjektumok létrehozására szolgáló műveleteket implementálja.
  • AbstractProduct: a termékobjektumok egy típusának interfészt deklarálja.
  • ConcreteProduct: definiálja a létrehozandó termékobjektumot a megfelelő konkrét factory (gyár) mellett, implementálja az AbstractProduct interfészt.
  • Client: csak az AbstractFactory és az AbstractProduct osztályok által deklarált interfészeket használja.
Együttműködési információk

Normál esetben a ConcreteFactory osztály egyetlen példányát hozza létre futásidőben. Ez a konkrét factory egyéni implementációval rendelkező termékobjektumokat készít. Különböző termékobjektumok létrehozásához a kliensnek különböző konkrét factory-kat kell használnia. Az AbstractFactory a termékobjektumok létrehozását a ConcreteFactory alosztályra bízza.

Következmények

Az Abstract Factory mintának a következő előnyei és hátrányai vannak:
  1. Elszigeteli a konkrét osztályokat. Az Abstract Factory minta segít az alkalmazások által létrehozott objektumosztályok irányítását. Mivel a factory egységbe zárja a termékobjektum létrehozásának felelősségét és folyamatát, így elszigeteli a klienst az implementációs osztályoktól. A kliensek a példányokat saját absztrakt interfészükön át kezelik. A termékosztályok nevei elszigeteltek a konkrét factory implementációjában; azok nem jelennek meg a kliens kódjában.
  2. Megkönnyíti a termékcsaládok cseréjét. A konkrét factory osztálya egy alkalmazásban egyszer jelenik meg – ahol példányosítottuk. Ez megkönnyíti az alkalmazás által használt konkrét factory megváltoztatását. A különböző termék használatához csak a konkrét factory-t kell megváltoztatni. Mivel az absztrakt factory komplett termékcsaládokat készít, egyszere megváltozik az egész termékcsalád.
  3. Támogatja a termékek közti konzisztenciát. Amikor egy családon belül a termékobjektumokat együttműködésre tervezték, akkor fontos, hogy az alkalmazás egy időben csak egy családhoz tartozó objektumokat használjon. Az AbstractFactory ezt könnyen érvényesíti.
  4. Az új termékfajták támogatása nehézkes. Az absztrakt factory kibővítése újfajta termékek létrehozására nem könnyű. Azért mert az AbstractFactory interfész rögzíti a létrehozható termékek halmazát. Az újfajta termékek támogatásához ki kell bővíteni a factory interfészt, amihez meg kell változtatni az AbstractFactory osztályt és annak minden alosztályát.
Implementációs rész
  1. Factory-k mint Singleton-ok. Egy alkalmazásnak tipikusan csak egyetlen egy ConcreteFactroy példányra van szüksége termékcsaládonként. Így azokat általában legjobb Singleton segítségével implementálni.
  2. A termékek létrehozása. Az AbstractFactory csak a termékek létrehozására szolgálóinterfészt deklarálja. A ConcreteProduct alosztály végzi valójában a létrehozást. A legáltalánosabb megoldás, ha minden termékhez deklarálunk egy factory metódust. Amíg az implementáció egyszerű, addig minden egyes termékcsaládhoz szükség van egy konkrét factory alosztályra, még akkor is ha a termékcsaládok csak alig térnek el.
  3. Ha sok termékcsaládra van szükség, a konkrét factory implementálható a Prototype minta felhasználásával. A konkrét factory a családba tartozó minden termékhez külön tartozó prototípus példány segítségével inicializálható, és a prototípus klónozásával hozható létre új termék. A prototípus alapú megközelítés kiküszöböli, hogy minden egyes új termékcsaládnak új konkrét factory-ra lenne szüksége.
  4. ővíthető factory-k definiálása. Az AbstractFactory általában különböző műveleteket definiál az általa létrehozható minden termékfajtához. A termékek típusa a műveletek szignatúrájába van kódolva. Egy új terméktípus hozzáadása estén szükség van az AbstractFactory interfész és az összes tőle függő osztály megváltoztatására.
  5. Egy rugalmasabb, de kevésbé biztonságos kivitelezés, ha az objektumokat létrehozó műveleteket paraméterezzük. Ez a paraméter határozza meg a létrehozandó objektum típusát. Ez lehet egy osztályazonosító, egy egész szám, egy sztring, vagy bármi ami azonosíthatja a termék típusát. Ezzel a megközelítéssel, az AbstractFactory-nak csak egy egyszerű „Make” műveletre van szüksége, ami a létrehozandó objektum típusát jelző paraméterrel rendelkezik. Ezt a technikát használjuk a fent megbeszélt Prototype- és osztály-alapú absztrakt factory-kban.
Példakód

Az elvont gyár minta segítségével most létrehozunk egy pályaszerkesztőt DXCoder példája alapján. Alaposztályok a megjelenítéshez:
class Sprite 
{
public Sprite(int size, string textureName) { }
}

static class SpriteFactory
{
public static Sprite CreateBall()
{
return new Sprite(20, "ball.png");
}

public static Sprite CreateWall()
{
return new Sprite(100, "wall.jpg");
}
}

class Game
{
Sprite[,] map = new Sprite[10, 10];

public void CreateLevel()
{
map[0, 0] = SpriteFactory.CreateWall();
map[1, 0] = SpriteFactory.CreateWall();
map[1, 1] = SpriteFactory.CreateBall();
}
}
Mint látható a Game osztályban már nem kell foglalkozni az egyes elemek részleteivel. A CreateLevel helyére el lehet képzelni egy olyan ciklus, ami egy file-ból tölti be a pályát. A másik trükk, hogy felhasználtuk az Egyke mintát is egyúttal.
Mi van akkor, ha a pályaszerkesztővel több pályát is szeretnénk készíteni egységes jelöléssel? A wall az mindig W, a ball az pedig mindig B. Többféle világ is van, ahol a ball az lehet forest, vagy robo is. Nem szeretnénk átírni se az editort, se a pálya betöltőt, úgyhogy a gyárak körül kell változtatni. A következő példa már az Elvont gyárat mutatja be:
  • AbstractFactory: IAbstractFactory
  • ConcreteFactory: RobotSpriteFactory, ForestSpriteFactory
  • Client: Game (CreateLevel)
Forrás:
using System;

namespace ConsoleApp
{
static class SingletonManager where T : new()
{
static T instance = Activator.CreateInstance();

public static T Instance
{
get { return instance; }
}
}

class Sprite
{
public Sprite(int size, string textureName) { }
}

interface IAbstractFactory
{
Sprite CreateBall();
Sprite CreateWall();
}

class ForestSpriteFactory : IAbstractFactory
{
public Sprite CreateBall()
{
return new Sprite(20, "ball.png");
}

public Sprite CreateWall()
{
return new Sprite(100, "wall.jpg");
}
}

class RobotSpriteFactory : IAbstractFactory
{
public Sprite CreateBall()
{
return new Sprite(26, "roboBall.png");
}

public Sprite CreateWall()
{
return new Sprite(120, "roboWall.jpg");
}
}

class Game
{
Sprite[,] map = new Sprite[10, 10];

public void CreateLevel(IAbstractFactory factory)
{
map[0, 0] = factory.CreateWall();
map[1, 0] = factory.CreateWall();
map[1, 1] = factory.CreateBall();
}
}

static class Program
{
static void Main()
{
var forestGame = new Game();
forestGame.CreateLevel(SingletonManager.Instance);

var robotGame = new Game();
robotGame.CreateLevel(SingletonManager.Instance);
}
}
}
Látható, hogy a CreateLevel lényegében semmit nem változott. Ha több 100 elemet kell betölteni, akkor se történik változás, mindig a világnak megfelelő objektumok fognak betöltődni. Itt még pluszban az Egyke minta egyik C# változatát is felhasználtuk, hogy ne kelljen állandóan fölösleges objektumokat létrehozni.

Kapcsolódó minták

Az Abstact Factory osztályokat gyakran factory metódusokkal (gyártó függvényekkel) valósítják meg, de implementálhatóak Protoype használatával is.

Felhasznált anyagok

0 megjegyzés :

Megjegyzés küldése