Wzorzec obserwator C++/Qt

Często się zdarza że programiści napotykają problem we współczesnych programu posiadające interfejs użytkownika (GUI), muszą reagować na zmiany danych w kilku różnych miejscach jednocześnie. Do sytuacji takiej może dojść wtedy gdy pracujemy na tych samych danych, które możemy edytować w kilku miejscach jednocześnie np. wykresy obrazujące takie same dane. Zmiana wartości na jednym wykresie implikuje zmianę wartości na pozostałych wykresach.

W przypadku kiedy mamy do czynienia z kilkoma obiektami możemy wykorzystać mechanizm sygnałów i slotów w Qt, jednak jeżeli jednak liczba obserwowanych obiektów, które się zmieniają nie jest zdefiniowana lub dotyczy znacznej grupy obiektów możemy zbudować osobną klasę zwaną Obserwatorem mającą na celu powiadamianie obiektów o występujących zmianach.

Pomimo że istnieje wiele różnych implementacji tego samego wzorca mają one elementy wspólne takie jak:

Wszystkie z obiektów można podzielić na dwie osobne grupy wydawców i klas pełniących rolę obserwatorów
Każda z implementacji zakłada że jest jeden obiekt, które informuje inne obiekty o zmianach
Mechanizm wysyłania informacji do pozostałych klas jest zdefiniowany w całości w klasie bazowej wydawcy.

Zalety i wady

Zalety:

Luźna zależność między obiektem obserwującym i obserwowanym. Ponieważ nie wiedzą one wiele o sobie nawzajem, mogą być niezależnie rozszerzane i rozbudowywane bez wpływu na drugą stronę.
Relacja między obiektem obserwowanym a obserwatorem tworzona jest podczas wykonywania programu i może być dynamicznie zmieniana.
Możliwość zablokowania klientowi drogi do bezpośredniego korzystania ze złożonego systemu, jeśli jest to konieczne.

Wady:

Obserwatorzy nie znają innych obserwatorów, co w pewnych sytuacjach może wywołać trudne do znalezienia skutki uboczne.
Zastosowanie

Wzorzec Obserwatora sprawdza się wszędzie tam, gdzie stan jednego obiektu uzależniony jest od stanu drugiego obiektu.

Implementacja

//Obserwator:
class Obserwator {
public:
    virtual void update() = 0;
};
//Obserwowany:

class Obserwowany {
protected:
    std::list <Obserwator*> obserwatorzy;
public:
    void dodaj(Obserwator *o) {
        obserwatorzy.push_back (o);
    }
    void usun(Obserwator *o) {
        obserwatorzy.remove (o);
    }
 
    void powiadom () {
        std::list<Obserwator *>::iterator it;
        for (it = obserwatorzy.begin(); it != obserwatorzy.end(); it++) {
            (*it)->update ();
        }
    }
};

Obserwowany Konkretny:

Dziedziczenie wielokrotne dostępne w C++ jest często używane w wzorcu Obserwatora

class Miod {
protected:
    int ilosc;
public:
    Miod (int q) {
        ilosc = q;
    }
 
    int pobierzIlosc () {
        return ilosc;
    }
 
    void ustawIlosc (int q) {
        ilosc = q;
    }
};
 
class ObserwowanyMiod : public Obserwowany, public Miod {
    std::string stan;
public:
    ObserwowanyMiod (int q) : Miod(q) {
    }
 
    std::string pobierzStan () {
        return stan;
    }
 
    void ustawStan (const std::string& s) {
        stan = s;
        std::cout << "Stan: " << stan << std::endl;
        powiadom();
    }
};

Obserwator Konkretny:

class ObserwatorMis : public Obserwator {
protected:
    int misId;
    ObserwowanyMiod *miod;
public:
    ObserwatorMis (ObserwowanyMiod *h, int id) {
        miod = h;
        misId = id;
    }
    void update () {
        std::string stan = miod->pobierzStan();
        if (!stan.compare ("Sa ludzie blisko miodu")) {
            std::cout << "Mis" << misId << ": Czekam w ukryciu" << std::endl;
        } else if (!stan.compare ("Nie ma ludzi blisko miodu")) {
            std::cout << "Mis" << misId << ": Kradne miod" << std::endl;
            miod->ustawIlosc(miod->pobierzIlosc()-1);
        } else if (!stan.compare ("Ida ludzie")) {
            std::cout << "Mis" << misId << ": Uciekam" << std::endl;
        }
    }
};

Przykład użycia:

int main() {
    ObserwowanyMiod miod (5);
    Obserwator *mis1 = new ObserwatorMis(&miod, 1);
    Obserwator *mis2 = new ObserwatorMis(&miod, 2);
 
    miod.dodaj(mis1);
    miod.dodaj(mis2);
 
    std::cout << "Ilosc miodu: " << miod.pobierzIlosc() << std::endl;
 
    miod.ustawStan("Sa ludzie blisko miodu");
    miod.ustawStan("Nie ma ludzi blisko miodu");
    miod.ustawStan("Ida ludzie");
 
    std::cout << "Ilosc miodu: " << miod.pobierzIlosc() << std::endl;
    delete mis1;
    delete mis2;
    return 0;
}

Pytania rekruterów na stanowisko C++/Qt Developer.

1. Co to jest dziedziczenie?
2. Czym jest polimorfizm?
3. Jakie znasz wzorce strukturalne?
5. Do czego służy rzutowanie?
6. Czy język C++ jest językiem obiektowym?
7. Co to jest kompilacja?
8. Czym jest linkowanie?
9. Co to jest zmienna statyczna?
10. Czym jest zakres funkcji?
11. Jak możemy przekazywać parametry do funkcji?
12. Czym jest przeciążenie funkcji?
13. Czym jest przeładowanie operatorów?
14. Czym jest funkcja wirtualna?
15. Co oznaczają słowa private, protected?
16. Co oznacza słówko const?
17. Czym są sygnały i sloty?
18. Na czym polega płytkie kopiowanie?
19. Czym są inteligentne wskaźniki?
20. Jaka różnica jest pomiędzy stosem a stertą?
21. Czym jest funkcja statyczna?
22. C++ 11 : typ auto, wyrażenia lambda, wielowątkowość.
22. Czym są mutexy?
23. Do czego służą szablony funkcji?
24. Czym są wycieki pamięci?
25. Czym są kontenery?
26. Na czym polega rekurencja?
27. Czym jest typ wyliczeniowy?
28. Co oznacza słówko inline?
29. Czym jest refraktoryzacja?
30. Czym jest konstruktor i destruktor?
31. Do czego służy destruktor wirtualny?
32. Czy konstruktor może być wirtualny?
33. Czym jest lista inicjalizacyjna?

Jak są jakieś propozycje pytań to proszę w komentarzach. Dopiszę do listy 😉

a więc dopisane do listy:

34. Czym jest inkrementacja, a czym dekrementacja?
35. Co powoduje słówko explicit przed konstruktorem?
36. Do czego służy profiler?

Wzorzec Adapter C++/Qt

Zastosowanie

Adapter inaczej nazywany Nakładką (ang. wrapper) to strukturalny wzorzec projektowy, którego zadaniem jest stworzenie spójnego interfejsu dla dwóch niekompatybilnych klas. Adapter przekształca interfejs jednej z klas na interfejs drugiej.

Adapter

Wskazówki praktyczne

Aby stworzyć wspólny interfejs warto zaimplementować klasę z metodami wirtualnymi która będzie częścią wspólną dla niewspółbieżnych klas. Adapter jest używany w przypadku gdy chcemy przysłonić już istniejące interfejsy jednym wspólnym, a nie możemy tego zrobić np. w sytuacji używania bibliotek zewnętrznych do których mamy tylko API. Autorzy często przytaczają przykład różnych wejść (HDMI, VGA) które mają takie same zadania ale różne interfejsy, a klient ma obsługiwać takie same metody (wyświetl, odepnij).

Zalety i wady

Zalety

  • Umożliwia współprace klas których wykorzystanie byłoby utrudnione ze względu na brak spójnych interfejsów.
  • Bardziej przejrzysty kod

Wady:

  • Wymagane jest dziedzicznie co powoduje przyrost plików z klasami (jeżeli stosujemy adapter klasowy)
  • Adapter klasowy jest mniej elastyczny niż Adapter obiektowy np. po dodaniu w naszym przykładzie getScreenShot(), zakrywanie metod wirtualnych jest bezsensowne w przypadku Mp3Player
#include <QDebug>
#include <QString>

class Player
{
    public:
        virtual void play(){}
        virtual void stop(){}
};

class FlashPlayer: public Player
{
    public:
        void play(){ qDebug() << "Play flash file"; }
        void stop(){ qDebug() << "Stop flash file"; }
};

// This is liblary and we cannot change implementatnion
class Mp3Player
{
    public:
        void playSound(){ qDebug() << "Play mp3 file"; }
        void stopSound(){ qDebug() << "Stop mp3 file"; }
};

/* Adapter */
class Mp3Adapter : public Mp3Player, public Player
{
public:
    void play(){playSound();}
    void stop(){stopSound();}

};

int main()
{
    Player *flash = new FlashPlayer();
    //  Player *mp3 = new Mp3Player();  // Błąd !!!
      Player *mp3 = new Mp3Adapter();

    QList <Player *> listPlayers{flash,mp3};

    foreach(Player *play, listPlayers)
        play->play();


    foreach(Player *play, listPlayers)
        play->stop();

    qDeleteAll(listPlayers);

    return 0;
}

Wyjście programu:

Play flash file
Play mp3 file
Stop flash file
Stop mp3 file

Wzorzec Fabryka abstrakcyjna i Metoda wytwórcza C++/Qt

Zastosowanie

Fabryka abstrakcyjna (ang. abstract factory) to kreacyjny wzorzec metody wytwórczej dostarcza abstrakcji do tworzenia obiektów nieokreślonych, ale powiązanych typów. Umożliwia także dziedziczącym klasom decydowanie jakiego typu ma to być obiekt. Jest on często używany wraz ze wzorce metody wytwórczej lub inaczej nazywanej metody fabrykującej (ang. factory method), która umożliwia zwracanie obiektów, lub przypisywanie im właściwości w zależności od parametru wysłanego.

Fabryka abstrakcyjna

Wskazówki praktyczne

Wzorzec jest stosowany wówczas gdy chcemy ujednolicić interfejs i uniezależnić interfejs od implementacji i składania obiektów. W przypadku metody wytwórczej stosujemy ją wówczas gdy nie wiemy jakie obiekty będą tworzone podczas używania programu (np. będzie delegatem lub tworzona przez wybór użytkownika). Dobrym przykładem jest generowania poprzez klasy konkretne plików html, pdf lub zwykłego tekstu, za pomocą wspólnego interfejsu (fabryki abstrakcyjnej).

Zalety i wady

Zalety

  • Odseparowanie klas konkretnych poprzez posługiwanie się interfejsami abstrakcyjnymi.
  • Łatwość rzutowania pomiędzy obiektami pochodnymi.
  • Spójność klas, w sytuacji gdy pożądane jest, aby klasy produkty były z określonej rodziny, fabryka bardzo dobrze to zapewnia.
  • Łatwość utrzymania kodu po dodaniu klasy pochodnej, oraz łatwe dodawanie implementacji logiki nowych klas pochodnych.

Wady:

  • Trudność w rozszerzaniu o nowe funkcjonalności klas pochodnych, spowodowana koniecznością rozszerzania interfejsów fabryki.
#include <QDebug>
#include <QString>

using namespace std;

// Abstract base class
class Oferta {
public:
    virtual const QString getOfertaPodstawowa() = 0;
    virtual const QString getDodatki() = 0;

    void print() {
        qDebug() << getOfertaPodstawowa() <<  getDodatki();
    }
    virtual ~Oferta(){qDebug() <<"Oferta usunięta";}

};

// Klasa konkretna
class OszczednaOferta : public Oferta {
public:
   const QString getOfertaPodstawowa() {
        return "Abonament 20zł";
    }

   const QString getDodatki() {
        return "Pakiet 60 minut i 30 sms-ów";
    }
   ~OszczednaOferta(){ qDebug() <<"Oszczędna oferta usunięta"; }
};

// Klasa konkretna
class SredniaOferta : public Oferta {
public:
    const QString getOfertaPodstawowa()
    {
        return "Abonament 50zł";
    }

   const QString getDodatki()
   {
        return "Pakiet 1000 minut i 1000 sms-ów";
    }
   ~SredniaOferta(){ qDebug() <<"Średnia oferta usunięta";}
};

// Klasa konkretna
class DrogaOferta : public Oferta {
public:
   const QString getOfertaPodstawowa() {
        return "Abonament 40zł";
    }

   const QString getDodatki() {
        return "Pakiet 300 minut i 200 sms-ów";
    }
   ~DrogaOferta(){ qDebug() <<"Droga oferta usunięta";}
};

// Abstract Factory returning a Oferta
class OfertaFactory {
public:
    Oferta* getOferta(const QString type)
    {
        if ( type == "Oszczędna" ) return new OszczednaOferta();
        else if ( type == "Średnia" ) return new SredniaOferta();
        else if ( type == "Droga" ) return new DrogaOferta();
        return NULL;
    }
    ~OfertaFactory(){

}

};

int main()
{
    OfertaFactory* myFactory = new OfertaFactory();

    Oferta* myOferta1 = myFactory->getOferta("Oszczędna");
    myOferta1->print();

    Oferta* myOferta2 = myFactory->getOferta("Średnia");
    myOferta2->print();

    Oferta* myOferta3 = myFactory->getOferta("Droga");
    myOferta3->print();

    delete myOferta1;
    delete myOferta2;
    delete myOferta3;

    return 0;
}

Wyjście programu:

"Abonament 20zł" "Pakiet 60 minut i 30 sms-ów"
"Abonament 50zł" "Pakiet 1000 minut i 1000 sms-ów"
"Abonament 40zł" "Pakiet 300 minut i 200 sms-ów"
Oszczędna oferta usunięta
Oferta usunięta
Średnia oferta usunięta
Oferta usunięta
Droga oferta usunięta
Oferta usunięta

Przykład 2

#include <iostream>
#include <QtCore>


using namespace std;

class Shape {
  public:
    Shape() {
      id_ = total_++;
    }
    virtual void draw() = 0;
  protected:
    int id_;
    static int total_;
};
int Shape::total_ = 0;

class Circle : public Shape {
  public:
    void draw() {
      cout << "circle " << id_ << ": draw" << endl;
    }
};
class Square : public Shape {
  public:
    void draw() {
      cout << "square " << id_ << ": draw" << endl;
    }
};
class Ellipse : public Shape {
  public:
    void draw() {
      cout << "ellipse " << id_ << ": draw" << endl;
    }
};
class Rectangle : public Shape {
  public:
    void draw() {
      cout << "rectangle " << id_ << ": draw" << endl;
    }
};

class Factory {
  public:
    virtual Shape* createCurvedInstance() = 0;
    virtual Shape* createStraightInstance() = 0;
};

class SimpleShapeFactory : public Factory {
  public:
    Shape* createCurvedInstance() {
      return new Circle;
    }
    Shape* createStraightInstance() {
      return new Square;
    }
};
class RobustShapeFactory : public Factory {
  public:
    Shape* createCurvedInstance()   {
      return new Ellipse;
    }
    Shape* createStraightInstance() {
      return new Rectangle;
    }
};

int main() {
#ifdef Q_OS_LINUX
  Factory* factory = new SimpleShapeFactory;
#elif Q_OS_ANDROID
  Factory* factory = new RobustShapeFactory;
#endif
  Shape* shapes[3];

  shapes[0] = factory->createCurvedInstance();   // shapes[0] = new Ellipse;
  shapes[1] = factory->createStraightInstance(); // shapes[1] = new Rectangle;
  shapes[2] = factory->createCurvedInstance();   // shapes[2] = new Ellipse;

  for (int i=0; i < 3; i++) {
    shapes[i]->draw();
  }
  for (int i=0; i < 3; i++)
    delete shapes[i];
}

Wzorzec projektowy Singleton C++ / Qt

Zastosowanie

Kreacyjny wzorzec projektowy, charakteryzujący się jedną instancją danego obiektu oraz globalnym dostępem do funkcji getInstance(), dzięki czemu możemy uzyskać do niego dostęp praktycznie z każdego miejsca. Inną cechą charakterystyczną jest Lazy loading, czyli alokacja pamięci dla singletona występuje dopiero przy jego pierwszym wywołaniu (odwołaniu się do niego za pomocą funkcji getInstance()).

Singleton

Własności singletona

ZALETY

  • jest tworzony przy pierwszym wywołaniu,
  • jest niszczony po zakończeniu działania programu,
  • po utworzeniu istnieje tylko i wyłącznie jego jedna instancja,
  • programista nie uczestniczy w procesie wywołania konstruktora, który zwykle jest prywatny lub zabezpieczony (protected), po to aby nie tworzyć więcej instancji obiektu singletona.

WADY

  • Rozhermetyzowanie klas poprzez globalny dostęp,
  • Trudniejsza analiza aplikacji podczas testów,
  • Może powodować problemy podczas wykorzystania go w wielowątkowych procesach.
#include <QDebug>
#include <QString>

class Ustawienia
{
public:
    static Ustawienia & getSingleton()
    {
        static Ustawienia singleton; // lazy Loading
        return singleton;
    }

    void setStyle(const QString style)
    {
        this->style = style;
    }
    QString getStyle(){return style;}

private:
    QString style;
    Ustawienia(): style("Unknown"){}
    ~Ustawienia(){}

};

class Klient
{
public:
    Klient( )
    {
    Ustawienia::getSingleton().setStyle("Blue");
    }

    void whiteStyle()
    {
        Ustawienia::getSingleton().setStyle("White");
    }
};

void funkcjaGlobalStyle()
{
    qDebug() << Ustawienia::getSingleton().getStyle();
}

int main()
{
    funkcjaGlobalStyle();

    Klient klient;
    funkcjaGlobalStyle();

    klient.whiteStyle();
    funkcjaGlobalStyle();

    Ustawienia::getSingleton().setStyle("Red");
    funkcjaGlobalStyle();

    return 0;
}

Rezultat

"Unknown"
"Blue"
"White"
"Red"

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;
}

Generating random number using srand() and rand() Qt

You can use random function from stdlib and fix time to maximize pseudo random operation in a short period of short time.

const int MainWindow::getNumber() const
{
qsrand( (unsigned)time(NULL) ^random());
return qrand() % max;
}

Using qsrand(QTime::currentTime().msec()) it can also be done, but in a short period of time the function will return the same value.

Recenzja książki „C++ i Qt – wprowadzenie do wzorców projektowych”

Qt wprowadzenie do wzorców projektowych

Książkę można byłoby zawrzeć w jednym zdaniu – jest to zdecydowanie najlepsza pozycja o Qt, którą czytałem. Choć zdjęcie wygląda, jakbym ją wypożyczył z biblioteki, gdzie była czytana co najmniej przez kilkadziesiąt osób, to jednak muszę przyznać, że jest to książka którą czytałem sam wielokrotnie, a jej wygląd wynika z wysokiej jakości treści i przykładów dobrych praktyk, do których wracałem.

„C++ i Qt wprowadzenie do wzorców projektowych” zawiera kompleksowe omówienie zasad tworzenia wysokiej jakości oprogramowania. Wyjaśnione są w niej najważniejsze wzorce projektowe, widgety, sposoby ich konstruowania, pokazany na przykładach język modelowania UML. Autorzy rekomendują też w większości narzędzia typu open source i podają wiele przykładów praktycznych.

ZALETY

Po przeczytaniu książki można wielokrotnie do niej wracać, ponieważ jest w niej tak dużo rozwiązań i solidnych przykładów, że mogłaby się  ona z powodzeniem nazywać biblią programisty.

REKOMENDACJA

Lekturę polecam osobom, które miały już styczność z programowaniem. Raczej co najmniej śrenio zaawansowanym programistom. Książkę mogę zarekomendować wszystkim, chcącym stać się lepszymi programistami C++/Qt.

OCENA

Bez zastanowienia: 10/10.

&nb