Wzorzec Budowniczy C++/Qt

Budowniczy

Wzorzec Budowniczy to wzorzec kreacyjny zapewniający kontrolę nad procesem tworzenia klas. Jest on przydatny, gdy chcemy zastosować wiele różnych konstruktorów, zapewniając większą czytelność kodu.

builder_uml_01

Elementy charakterystyczne

W tym wzorcu możemy wyróżnić następujące elementy:
Budowniczy – interfejs abstrakcyjny do tworzenia części składowych obiektu złożonego, w naszym przykładzie jest to klasa Pizza
(produktu)
Konkretny budowniczy – jego celem jest konstruowanie i zestawianie części produktu poprzez implementowanie interfejsu Budowniczego. Definiuje i kontroluje on tworzoną przez siebie
reprezentację. Konkretnych budowniczych może być wiele, w naszym przykładzie jest to HawajskaPizza, OstraPizza i MexicoPizza.
Dyrektor – konstruuje obiekty, używając interfejsu Budowniczego do wywoływania metod konkretnego budowniczego oraz nadzoruje proces budowy aby proces konstrukcyjny przebiegał w odpowiedniej sekwencji. W naszym przypadku jest to kelner.
Produkt – złożony obiekt który tworzony jest z wielu części składowych. Jest on konstruowany przez Dyrektora przy użyciu konkretnego budowniczego. W naszym przypadku jest to Pizza którą zwaraca getProduct();

Zalety

  • Różnorodność implementacji wewnętrznych struktur klas.
  • Łatwość konwersji tych samych danych do kilku różnych postaci. Np. jeden format tekstu na różne typy wyjściowe PDF, HTML, Plain text.
  • Duża skalowalność dzięki elastyczności w modyfikacjach (dodawanie nowych reprezentacji klas jest uproszczone). Gdy Chcemy sobie zagwarantować możliwość łatwego dodawania obsługi nowego formatu danych wyjściowych, zapobiegając jednocześnie jakimkolwiek modyfikacjom klasy zarządzającej obiektami budowniczymi.
  • Większa możliwość kontrolowania tego, w jaki sposób tworzony jest obiekt (proces konstrukcyjny jest niezależny od elementów, z których składa się tworzony obiekt.

Wady

  • Duża liczba obiektów reprezentujących konkretne produkty.
  • Redundancja – nieumiejętne używanie wzorca może spowodować nieczytelność kodu (jeden produkt może tworzony przez zbyt wielu budowniczych).

Zastosowanie

Wzorzec budowniczego stosowany jest do oddzielenia sposobu tworzenia obiektów od tego jak te obiekty mają wyglądać. Używamy go wtedy gdy obiekty różnią się finalnie, ale zawierają takie same elementy i chcemy je poskładać z takich samych części np. gdy składamy komputer to robimy to z różnych elementów, ale finalnie osiągamy ten sam produkt – komputer. Tak samo jest ze składaniem samochodów ( rożne silniki, amortyzatory, światła), jadłospisów (różne elementy tj. ziemiaki z mięsem lub ryż z mięsem, różne rodzaje surówek).

#include <QDebug>
#include <QString>
#include <QList>

class Pizza
{
public:
    enum SKLADNIK{
        PIECZARKI = 1,
        SER = 2,
        SZYNKA = 4,
        PEPERONI = 8,
        KUKURYDZA = 16,
        TUNCZYK = 32,
        KURCZAK = 64,
        BROKULY = 128,
        JAJKO = 256,
        CEBULA = 512,
        ANANAS = 1024,
        OREGANO = 2048,
        FASOLA = 4096,
        SALAMI = 8192
    };

    enum SOS{
        CZOSNKOWY = 1,
        POMIDOROWY = 2,
        OLIWA = 4
    };

    const QString getSkladniki(const int skladnik)
    {
                switch(skladnik)
                {
                case PIECZARKI:
                    return "Pieczarki";
                case SER:
                    return "Ser";
                case SZYNKA:
                    return "Szynka";
                case PEPERONI:
                    return "Peperoni";
                case KUKURYDZA:
                    return "Kukurydza";
                case TUNCZYK:
                    return "Tuńczyk";
                case KURCZAK:
                    return "Kurczak";
                case BROKULY:
                    return "Brokuły";
                case JAJKO:
                    return "Jajko";
                case CEBULA:
                    return "Cebula";
                case ANANAS:
                    return "Ananas";
                case SALAMI:
                    return "Salami";
                case FASOLA:
                    return "Fasola";
                case OREGANO:
                    return "Oregano";
                default:
                    return "Niezdefiniowany";
                }
    }

    const QString getSosy(const int sos)
    {
                switch(sos)
                {
                case CZOSNKOWY:
                    return "Czosnkowy";
                case POMIDOROWY:
                    return "Pomidorowy";
                case OLIWA:
                    return "Oliwa";
                default:
                    return "Niezdefiniowany";
                }
    }

private:
    QString nazwa;
    bool ciasto;
    int sos;
    int skladniki;

public:
    Pizza() { }
    ~Pizza() { }

    void setCiasto(bool grube = false)
    {
        ciasto = grube;
    };
    void setSos(const int sklad) { this->sos = sos; };
    void setDodatki(const int sklad) { skladniki = sklad;  };
    void setDodatkiNowe(const int sklad) { skladniki += sklad;  };
    void setNazwa(const QString &value) { nazwa = value; };

    void showPizza()
    {
        QString nazwy;
        QString sosy;
        for(int i = 1; i <= SALAMI;i*=2)
            if(i & skladniki)
                nazwy += (nazwy.isEmpty() ? "" : ", ") + getSkladniki(i);

        for(int i = 1; i <= OLIWA;i*=2)
            if(i & skladniki)
                sosy += (sosy.isEmpty() ? "" : ", ") + getSosy(i);

        qDebug() << "Pizza" << nazwa
                 << " z grubym ciastem: " << (ciasto ? "Tak" : "Nie")
                 << "\nSos: " << sosy
                 << " Dodatki " << nazwy << "\n";
    }
};
// Builder
class PizzaBuilder
{
protected:
    Pizza * pizza;
public:
    PizzaBuilder() {}
    virtual ~PizzaBuilder() {}
    Pizza *getPizza() { return pizza; }

    void createNewPizzaProduct() { pizza = new Pizza; }

    virtual void buildNazwa()=0;
    virtual void buildCiasto()=0;
    virtual void buildSos()=0;
    virtual void buildDodatki()=0;

};

class HawajskaPizza : public PizzaBuilder
{
public:
    HawajskaPizza() : PizzaBuilder() {}
    ~HawajskaPizza(){}

    void buildNazwa() { pizza->setNazwa("Hawajska"); };
    void buildCiasto() { pizza->setCiasto(true); }
    void buildSos() { pizza->setSos(Pizza::CZOSNKOWY); }
    void buildDodatki() { pizza->setDodatki(Pizza::SER | Pizza::SZYNKA | Pizza::ANANAS  | Pizza::OREGANO); }
};
class OstraPizza : public PizzaBuilder
{
public:
    OstraPizza() : PizzaBuilder() {}
    ~OstraPizza() {}

    void buildNazwa() { pizza->setNazwa("Ostra"); };
    void buildCiasto() { pizza->setCiasto(false); }
    void buildSos() { pizza->setSos(Pizza::CZOSNKOWY | Pizza::POMIDOROWY); }
    void buildDodatki() { pizza->setDodatki(Pizza::SER | Pizza::CEBULA | Pizza::KUKURYDZA | Pizza::OREGANO); }
};

class MexicoPizza : public PizzaBuilder
{
public:
    MexicoPizza() : PizzaBuilder() {}
    ~MexicoPizza() {}

    void buildNazwa() { pizza->setNazwa("Meksykańska"); };
    void buildCiasto() { pizza->setCiasto(true); }
    void buildSos() { pizza->setSos(Pizza::OLIWA); }
    void buildDodatki() { pizza->setDodatki(Pizza::SER | Pizza::PEPERONI | Pizza::SALAMI | Pizza::FASOLA); }
};

// Director
class Kelner
{
private:
    PizzaBuilder* pizzaBuilder;
public:
    Kelner() : pizzaBuilder(NULL) {}
    ~Kelner() { }

    void setPizzaBuilder(PizzaBuilder* b) { pizzaBuilder = b; }
    Pizza* getPizza() { return pizzaBuilder->getPizza(); }
    void constructPizza()
    {
        pizzaBuilder->createNewPizzaProduct();
        pizzaBuilder->buildNazwa();
        pizzaBuilder->buildCiasto();
        pizzaBuilder->buildSos();
        pizzaBuilder->buildDodatki();
    }
};


int main()
{
    Kelner kelner;

    HawajskaPizza hawajskaPizza;
    kelner.setPizzaBuilder (&hawajskaPizza);
    kelner.constructPizza();
    Pizza* pizza = kelner.getPizza();
    pizza->showPizza();

    OstraPizza ostraPizza;
    kelner.setPizzaBuilder(&ostraPizza);
    kelner.constructPizza();
    pizza = kelner.getPizza();
    pizza->showPizza();

    MexicoPizza mexicoPizza;
    kelner.setPizzaBuilder(&mexicoPizza);
    kelner.constructPizza();
    kelner.getPizza()->setDodatkiNowe(Pizza::KURCZAK);
    pizza = kelner.getPizza();
    pizza->showPizza();

    return EXIT_SUCCESS;
}

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *