Wzorce projektowe (4)

Autor: Damian Chodorek • Opublikowany: 20 lipca 2015 • Kategoria: kursy, wzorce projektowe

Fabryka obiektów.

Fabryka obiektów to wzorzec wzorzec klasowy. Jego celem jest dynamiczne tworzenie obiektów różnego typu. Wykorzystywana jest w sytuacjach gdzie pewna klasa (np. biblioteczna) ma nie tylko operować na obiektach klienta ale także tworzyć je.

Istnieje kilka popularnych implementacji:

  • prosta, korzystająca z instrukcji if/else, nie da się bez zmiany jej kodu dodawać nowych obiektów,
  • registration with mirroring – w fabryce rejestrujemy produkt; klasa, która odpowiada za tę czynność jest klasą produktu,
  • registration without mirroring – tak samo, z tym że rejestracja produktu odbywa się poza jego klasą.

Klasy

class Product{/*
abstrakcyjna klasa, po której dziedziczą wszystkie produkty
*/};

class ConcreteProduct: public Product{/*
konkretny produkt, który implementuje interfejs klasy Product
*/};

class Factory{/*
gromadzi zarejestrowane produkty oraz na podstawie parametru (id) tworzy i zwraca odpowiedni produkt
*/};

class Client{/*
używa interfejsu Factory, przekazuje klasie parametr i w zamian otrzymuje referencję do utworzonego produktu
*/};

Etapy działania

  1. Klient tworzy obiekt fabryki.
  2. Rejestruje wszystkie obiekty, których jeszcze nie ma w fabryce.
  3. Przekazuje ID w postaci parametru do odpowiedniej metody i dostaje w zamian obiekt pożądanego typu.

Konsekwencje

  • Zwalnia klienta ze znajomości nazw klas. Wystarczy znajomość ID, które odpowiada danej klasie.
  • Fabryka gromadzi coś (konkretny obiekt, funktor, wskaźnik do funkcji) co odpowiada za tworzenie obiektu oraz ID.

Przykład

#include <map>
#include <iostream>
using namespace std;

///abstrakcjna klasa Product
class Document
{
  public:
    virtual Document* createDocument()=0;
    virtual void name()=0;
    virtual ~Document() {}
};

///konkretny produkt
class HTMLDocument: public Document
{
  public:
    HTMLDocument() : Document()
    {
    }

    Document* createDocument()
    {
      return new HTMLDocument();
    }

    void name()
    {
      cout<<"- HTML document"<<endl;
    }
};

///konkretny produkt
class PDFDocument: public Document
{
  public:
    PDFDocument() : Document()
    {
    }

    Document* createDocument()
    {
      return new PDFDocument();
    }

    void name()
    {
      cout<<"- PDF document"<<endl;
    }
};

///fabryka obiektów
class DocumentFactory
{
  public:
    void registerDocument(string documentType, Document* obj)
    {
      _map[documentType]=obj;
    }

    Document* createDocument(string documentType)
    {
      ///należy zabezpieczyć się przed podaniem nieprawidłowej nazwy
      return _map[documentType]->createDocument();
    }

    ~DocumentFactory()
    {
      for(map<string, Document*>::iterator iter=_map.begin();
          iter!=_map.end(); ++iter)
      {
          delete ((*iter).second);
      }
    }

  private:
    map<string, Document *> _map;
};

int main(int argc, const char* argv[])
{

  DocumentFactory df;
  df.registerDocument("PDF", new PDFDocument);
  df.registerDocument("HTML", new HTMLDocument);

  Document * currentDoc=df.createDocument("HTML");

  currentDoc->name();

  delete currentDoc;
}

W kolejnej części opisałem jak wygląda generalizacja tego wzorca, przy użyciu szablonów.

część 5

Dodaj komentarz

Twój adres e-mail nie zostanie opublikowany.