A factory method esetén láthattuk, hogy hogyan tudunk egy komponensbe kiszervezni egy vagy több közös őssel rendelkező objektum létrehozását.
De mi van akkor, ha ezen osztályok létrehozásának módja valami miatt el kell, hogy térjen?
Tételezzük fel, hogy egy grafikus alkalmazást Ãrunk, ami ablakokból áll. Azonban az ablak létrehozásának módja platformonként eltér. Nem ugyanaz az API áll rendelkezésre Windows és Linux operációs rendszeren ezek létrehozására.
Ennek a problémának az áthidalásában segÃt nekünk az abstract factory minta, mégpedig a factory absztrakciójával.
Nézzük meg, hogy ez hogyan nézne ki kódban:
interface IUiElement
{
void Render();
}
interface IWindow : IUiElement
{
IList<IUiElement> Elements { get; }
}
class WindowsWindow : IWindow
{
public IList<IUiElement> Elements { get; }
public WindowsWindow()
{
Elements = new List<IUiElement>();
}
public void Render()
{
Console.WriteLine("WindowsWindow Render");
foreach (var element in Elements)
{
element.Render();
}
}
}
class LinuxWindow : IWindow
{
public IList<IUiElement> Elements { get; }
public LinuxWindow()
{
Elements = new List<IUiElement>();
}
public void Render()
{
Console.WriteLine("LinuxWindow Render");
foreach (var element in Elements)
{
element.Render();
}
}
}
interface IUiFactory
{
IWindow CreateWindow();
}
class WindowsUiFactory : IUiFactory
{
public IWindow CreateWindow()
{
return new WindowsWindow();
}
}
class LinuxUiFactory : IUiFactory
{
public IWindow CreateWindow()
{
return new LinuxWindow();
}
}
class Application
{
private readonly IUiFactory _factory;
public Application(IUiFactory factory)
{
_factory = factory;
}
public void Run()
{
var window = _factory.CreateWindow();
window.Render();
}
}
Az IUiElement interfész az összes megjelenÃthetÅ‘ dolog közös viselkedését definiálja, mégpedig a kirajzolást. Ennek sok jelentÅ‘sége azonban nincs a Factory szempontjából.
Az IWindow a tÃpus, amit az IUiFactory interfészünk CreateWindow metódusa vissza fog adni. Az IWindow két konkrét implementációval rendelkezik, ezek a WindowsWindow és LinuxWindow.
Az IUiFactory szintén két konkrét implementációval rendelkezik és maga az Application osztály, ami a fő logikát tartalmazza erre függ.
A példa ugyan nem mutatja, de például a program indÃtásakor az Application osztály számára a platformhoz megfelelÅ‘ IUiFactory példányt adnánk oda.
Ezzel a módszerrel az Application osztálynak nem kell tudnia a platform függÅ‘ részekrÅ‘l vagy a konkrét elemek implementációjáról, elég csak a viselkedésüket ismernie az interfészen keresztül. A tényleges példányosÃtást pedig majd elvégzi az abstract factory.
Az abstract factory interfészt biztosÃt a termékcsalád minden osztályából objektumok létrehozásához. Mindaddig, amÃg a kód objektumokat hoz létre ezen a felületen keresztül, nem kell attól tartanunk, hogy egy-egy adott objektum nem megfelelÅ‘ változata jönne létre.
Előnyök
- Open/Closed elvet követi, mivel újabb tÃpusok bevezetése nem töri a kód meglévÅ‘ részét
- Követi a Single responsibility-t, mivel az osztály létrehozás egy komponens feladata
- SegÃt elkerülni a szoros kapcsolatot az osztály létrehozó és fogyasztó között
- Biztosak lehetünk benne, hogy a factory által létrehozott objektumok kompatibilisek egymással
Hátrányok
- AA kód bonyolultabbá válhat a kelleténél, mivel sok új interfész és osztály kerül bevezetésre a mintával együtt