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:
- 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.
- 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.
- 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.
- 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
- 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.
- 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.
- 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.
- ő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.
- 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 SpriteMint 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.
{
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();
}
}
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)
using System;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.
namespace ConsoleApp
{
static class SingletonManagerwhere 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);
}
}
}
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