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