Absztrakt gyár programtervezési minta

Az Absztrakt gyár (angolul Abstract factory) programtervezési minta módot nyújt arra, hogy egységbe zárjuk közös témához kapcsolódó egyedi gyártó metódusok egy csoportját anélkül, hogy specifikálnák azok konkrét osztályait.[1] Normál használatban, a kliens szoftver létrehozza az absztrakt gyár egy konkrét implementációját, és aztán a gyár általános interfészét használja a témához kapcsolódó konkrét objektumok létrehozásához. A kliens nem tudja (vagy nem törődik vele), milyen konkrét objektumokat kap ezekből a belső gyárakból, mivel csak a termékeik általános interfészét használja.[1] Ez a tervezési minta szétválasztja egymástól objektumok egy csoportjának implementációját azok általános használatától és objektum összetételre hagyatkozik, mivel az objektumok létrehozása olyan metódusokban van implementálva, amik a gyár interfészén vannak ismertté téve számára.[1] Egy példa az absztrakt gyár mintára, egy DokumentumLétrehozó nevű absztrakt osztály lehetne, ami interfészt nyújt többféle termék létrehozásához (pl.készítsLevelet() és készítsÖsszefoglalót()). A rendszerben bármennyi DokumentumLétrehozó osztályból származtatott konkrét gyár-változat lehet, mint például DíszesDokumentumLétrehozó vagy ModernDokumentumLétrehozó, mindegyikük különböző implementációval a készítsLevelet() és készítsÖsszefoglalót() metódusokra, amik elkészítik majd a megfelelő objektumokat mint például a DíszesLevél vagy a ModernÖsszefoglaló. Ezeknek a termékeknek mindegyike egy-egy egyszerű absztrakt osztályból van származtatva, mint például a Levél vagy az Összefoglaló, amik ismertek a kliens számára. A kliens kódja egy a témának megfelelő példányt fog kapni a DokumentumLétrehozó osztály egy származtatott konkrét osztályából, és annak a gyártó metódusait hívja majd meg. Az eredményül kapott objektumok mindegyike, ezen DokumentumLétrehozó osztály-implementáció által kerül majd legyártásra, amik megfelelnek majd a közös témának (például mindannyian „Díszes” vagy „Modern” objektumok lesznek). A kliensnek csak annyit kell tudnia, hogy hogyan kezelje az absztrakt Levél vagy Összefoglaló osztályokat, nem kell ismernie a specifikus változatokat, amiket a konkrét gyártól kapott.[2]

Egy gyár, egy konkrét osztály helye a kódban, ahol az objektumok létrejönnek. Az absztrakt gyár minta használatának szándéka arra irányul, hogy elszigetelje egymástól az objektumok létrehozását azok használatától, és hogy egymással összefüggő objektumok családjait hozza létre anélkül, hogy azok konkrét osztályaitól függene.[1] Ez lehetővé teszi új származtatott típusok bevezetését, anélkül, hogy az ős osztályokat használó kódot meg kellene változtatni.

Ennek a mintának a használata, lehetővé teszi egy rendszerben a konkrét típus implementációk kicserélését (még akár futásidőben is) anélkül, hogy az őket használó kódot módosítanánk. Mindazonáltal ennek a mintának (és a hasonló programtervezési mintáknak) a használata, szükségtelen komplexitást okozhat, és plusz munkát igényelhet a kezdeti kódkészítésben. Továbbá az absztrakció és a szétválasztás magasabb szintje olyan rendszert eredményezhet, amiben nehezebb a hibakeresés és a karbantartás.

Definíció szerkesztés

Az Absztrakt gyár minta lényege, hogy „Interfészt adjon ahhoz, hogy egymással összefüggő objektumok családjait hozzuk létre anélkül, hogy specifikálnánk a konkrét osztályaikat.”.[3]

Használat szerkesztés

A gyár határozza meg a létrehozásra kerülő objektum tényleges konkrét típusát, és a gyár az a hely ahol az objektum ténylegesen létrejön (például a C++ nyelven, a new művelettel). Mindazonáltal, a gyár csak egy absztrakt mutatót (pointert) ad vissza, ami a létrehozott konkrét objektumra mutat.

Ez elszigeteli a klienst az objektum létrehozástól azáltal, hogy a kliensnek meg kell kérnie egy gyár objektumot, hogy készítse el az általa kívánt absztrakt típust és adjon vissza számára egy absztrakt mutatót az objektumra.[4]

Mivel a gyár csak egy absztrakt mutatót (pointert) ad vissza, a kliens kód (ami az objektumot kérte a gyártól) nem ismeri az éppen létrehozott objektum aktuális, konkrét típusát. Mindazonáltal az absztrakt gyár ismeri a konkrét objektum típusát (ennélfogva a konkrét gyárat is); például az absztrakt gyár beolvashatja ezeket az adatokat egy konfigurációs fájlból. A kliensnek így nem kell típust specifikálnia, mivel az már megadásra került a konfigurációs fájlban. Részleteiben ez a következőt jelenti:

  • A kliens kódnak semmiféle tudomása nincs a konkrét típusról, nem szükséges meghivatkoznia vagy tartalmaznia semmilyen ezzel kapcsolatos header fájlt vagy osztály deklarációt. A kliens kód csak az absztrakt típussal dolgozik. A konkrét típusnak megfelelő objektumok ténylegesen a gyár által kerülnek létrehozásra, de a kliens kód képes használni ezeket az absztrakt interfészükön keresztül (kizárólag ezen keresztül, mivel csak ezt ismeri).[5]
  • Új konkrét típusok hozzáadása (használata) a kliens kód módosításával történik úgy, hogy azok egy másik gyárat használjanak mint eddig, amely módosítás jellemzően egyetlen sort érint, egyetlen fájlban. A másik (módosított) gyár aztán eltérő konkrét típus hoz majd létre, de még mindig ugyanolyan absztrakt típusra irányuló mutatót ad vissza, mint annak előtte — ezáltal elszigeteli a kliens kódját a változástól. Ez jelentősen egyszerűbb, mint módosítani a kliens kódját arra, hogy újfajta típust hozzon létre, ami változtatást kívánna meg minden helyen a kódban, ahol az újfajta objektum létrehozásra kerül (ami mellett biztosítani kellene azt is, hogy az összes ilyen kód-hely tudomással rendelkezzen az újfajta konkrét típusról, például azzal, hogy a forráskódokban mindenhol meghivatkozzuk az újfajta osztály header fájlját). Ha minden gyártó objektum globálisan tárolva van egy Egyke (singleton) objektumban, és az összes kliens kód ezen keresztül éri el a megfelelő gyárat az objektum létrehozáshoz, akkor a gyárak (ezáltal a gyártott konkrét típusok) megváltoztatása ennek az Egyke objektumnak a megváltoztatására egyszerűsödik.[5]

Felépítése szerkesztés

Rational Osztály Diagram szerkesztés

 

A createButton függvény a GuiFactory interfészben Button típusú objektumokat ad vissza. Az, hogy a Button objektumnak milyen konkrét implementációja kerül visszaadásra, a GuiFactory osztály implementációján múlik, ami a függvényhívást kezeli.

UML Osztály Diagram szerkesztés

 

Lepus3 Diagram (jelmagyarázat) szerkesztés

 

Pszeudokód szerkesztés

A kód egy gombot (Button) készít, amely Windows vagy Mac OS X stílusú lehet, attól függően, hogy melyik gyárat használjuk. Vegyük észre, hogy az alkalmazásnak (Application) semmilyen tudomása nincs arról, hogy milyen „GuiFactory” gyár van számára megadva (mi a konkrét implementációja), mint ahogy arról sem, hogy az konkrétan milyen gombot (Button) gyárt.

interface Button is
  method paint()

interface GUIFactory is
  method createButton()
      output:  a button

class WinFactory implementing GUIFactory is
  method createButton() is
      output:  a Windows button
    Return a new WinButton

class OSXFactory implementing GUIFactory is
  method createButton() is
      output:  an OS X button
    Return a new OSXButton

class WinButton implementing Button is
  method paint() is
    Render a button in a Windows style

class OSXButton implementing Button is
  method paint() is
    Render a button in a Mac OS X style

class Application is
  constructor Application(factory) is
      input:  the GUIFactory factory used to create buttons
    Button button := factory.createButton()
    button.paint()

Read the configuration file
If the OS specified in the configuration file is Windows, then
  Construct a WinFactory
  Construct an Application with WinFactory
else
  Construct an OSXFactory
  Construct an Application with OSXFactory

C# példa szerkesztés

Az Absztrakt gyár az alap Gyár minta bővítése. Gyár interfészeket nyújt egymással összetartozó osztályok gyártásához. Más szavakkal, interfészeket deklarálok a Gyárakhoz, amiket aztán hasonló módon használok mint az alap Gyár mintánál.

/*IVSR:Abstract factory pattern*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace DesignPatterns.AbstractFactory
{
    public class GenericFactory<T> 
        where T : new()
    {
        public T CreateObject()
        {
            return new T();
        }
    }

    public abstract class CarFactory
    {
        public abstract SportsCar CreateSportsCar();
        public abstract FamilyCar CreateFamilyCar();
    }
    
    public abstract class FamilyCar
    {
        public abstract void Speed(SportsCar abstractSportsCar);
    }

    public abstract class SportsCar
    {
    }

    public class MercedesFactory : CarFactory
    {
        public override SportsCar CreateSportsCar()
        {
            return new MercedesSportsCar();
        }

        public override FamilyCar CreateFamilyCar()
        {
            return new MercedesFamilyCar();
        }
    }

     
    class MercedesSportsCar : SportsCar
    {
    }

    class MercedesFamilyCar : FamilyCar
    {
        public override void Speed(SportsCar abstractSportsCar)
        {
            Console.WriteLine(GetType().Name + " is slower than "
                + abstractSportsCar.GetType().Name);
        }
    }

    public class Driver
    {
        private CarFactory _carFactory;
        private SportsCar _sportsCar;
        private FamilyCar _familyCar;

        public Driver(CarFactory carFactory)
        {
            _carFactory = carFactory;
            _sportsCar = _carFactory.CreateSportsCar();
            _familyCar = _carFactory.CreateFamilyCar();
        }

        public CarFactory CarFactory
        {
            get { return _carFactory; }
            set { _carFactory = value; }
        }

        public SportsCar SportsCar
        {
            get { return _sportsCar; }
        }

        public FamilyCar FamilyCar
        {
            get { return _familyCar; }
        }

        public void CompareSpeed()
        {
            FamilyCar.Speed(SportsCar);
        }
    }
}

A gyártó metódusok szintén egy közös interfész alapján vannak implementálva, melyek mindegyike objektumokat ad vissza.

    //IVSR: Abstract factory using common interface
    public interface IPeopleFactory
    {
        IPeople GetPeople();
    }

    public class VillagersFactory : IPeopleFactory
    {
        public IPeople GetPeople()
        {
            return new Villagers();
        }
    }

    public interface IProductFactory
    {
        IProduct GetProduct();
    }

    public class AppleFactory : IProductFactory
    {
        public IProduct GetProduct()
        {
            return new IPhone();
        }
    }

    public abstract class AbstractFactory
    {
        public abstract IPeopleFactory GetPeopleFactory();
        public abstract IProductFactory GetProductFactory();
    }

    public class ConcreteFactory : AbstractFactory
    {
        public override IPeopleFactory GetPeopleFactory()
        {
            return new VillagersFactory ();
        }

        public override IProductFactory GetProductFactory()
        {
            return new AppleFactory ();
        }
    }

Kapcsolódó szócikkek szerkesztés

Jegyzetek szerkesztés

  1. a b c d (2004) „Head First Design Patterns” (puha kötésű könyv) 1, 156. o, Kiadó: O'REILLY. (Hozzáférés: 2012. szeptember 12.)  
  2. (2004) „Head First Design Patterns” (paperback) 1, 162. o, Kiadó: O'REILLY. (Hozzáférés: 2012. szeptember 12.)  
  3. Gamma, Erich: Design Patterns: Abstract Factory. informIT, 2009. október 23. [2012. május 16-i dátummal az eredetiből archiválva]. (Hozzáférés: 2012. május 16.) „Object Creational: Abstract Factory: Intent: Provide an interface for creating families of related or dependent objects without specifying their concrete classes.”
  4. Veeneman, David: Object Design for the Perplexed. The Code Project, 2009. október 23. [2011. február 21-i dátummal az eredetiből archiválva]. (Hozzáférés: 2012. május 16.) „The factory insulates the client from changes to the product or how it is created, and it can provide this insulation across objects derived from very different abstract interfaces.”
  5. a b Abstract Factory: Implementation. OODesign.com. (Hozzáférés: 2012. május 16.)

Fordítás szerkesztés

Ez a szócikk részben vagy egészben az Abstract factory pattern című angol Wikipédia-szócikk ezen változatának fordításán alapul. Az eredeti cikk szerkesztőit annak laptörténete sorolja fel. Ez a jelzés csupán a megfogalmazás eredetét és a szerzői jogokat jelzi, nem szolgál a cikkben szereplő információk forrásmegjelöléseként.