21 Zestawienie Różne żródła

tematy:
Beautiful C++: Updating Legacy Code pluralsight Link
Bjarne Stroustrup: C++11 Style - konferencja





******************

Beautiful C++: Updating Legacy Code
(podobne do C vs C++ z  rozdziału LINK )

-strcpy - z punktu widzenia c++ niebezpieczne (w VC++ uzyj strcpy_s)
-Słowa kluczowe w C++ Link
-deffine nie mają typu i zakresu - uzywaj Const static member
-zamiast Enum użyj enum class (bezpieczne typowanie)
-zamiast makrofunkcji - lambda         [&]() { tresc }  albo Template jeśli potrzebujesz różnych typów

-c++ cechy:
    definiowane własne typy
    RAII * (deterministic destruction )
    Polimorphism (virtualne funkcje i chierarchia dziedziczenia, posługiwanie się ogólnym typem)
    Templates
    Const-correctness -using the keyword const to prevent const objects from getting mutated LINK

*RAII - Przejęcie zasobu jest połączone z konstrukcją, a zwolnienie z automatyczną destrukcją zmiennej. Ponieważ wywołanie destruktora jest automatyczne, gdy zmienna wyjdzie poza swój zasięg, jest zagwarantowane, że zasób zostanie zwolniony od razu gdy skończy się czas życia zmiennej. Stanie się tak również przy wystąpieniu wyjątku. RAII jest kluczową koncepcją przy pisaniu kodu odpornego na błędy. Technika RAII jest używana na przykład przy zakładaniu blokad wątków albo obsłudze plików. Własność pamięci przydzielanej dynamicznie (za pomocą new w C++) może być również kontrolowana za pomocą RAII. Do tego celu biblioteka standardowa C++11 definiuje inteligentne wskaźniki: std::unique_ptr dla obiektów z jednym właścicielem, oraz std::shared_ptr dla obiektów współdzielonych
-czyli twórz w konstruktorze, zwalniaj w destruktorze (destruktor wywoływany automatycznie po przekorczeniu zakresu )
-rozważ sposób tworzenia kopii (płytkie - głębokie) rozważ niekopiowalność (2 konstruktory = delete)  Resource(Resource const& other) = delete;  Resource& operator=(Resource const& other) = delete;

-c++ tworzenie klasy z kodu strukturalnego (funkcje)
    wrzuć zmienne do przestrzeni publicznej, powiązame zmienne stwórz jako nowych memberów (zainicjalizuj w konstruktorze - liście inicjalizacyjnej)
    dodaj publiczne metody na podstawie istniejacych funkcji
    zamien zmienne na prywatne - smprawdz czy działa (enkapsulacja)
    jeśli trzeba dodaj settery i gettery do nich (ale tylko jeśli potrzeba)
    zmienn nazwy zmiennych na klasowe ( m_  )

-jeśli powtarzarz istnienie wielu podobnych funkcji, rozważ dziedziczenie
-uzywaj const gdzie tylko mozesz, dodawaj to na samym początku tworzenia (nie tylko zmienne, ale i funkcje - które nie zmianiają niczego )
-nie używaj Typedef dla structur (styl C)
-uzywaj poprawnego castowania (c++ style   static_cast<>  dynamic_cast<>  reinterpret_cas<>  const_cast<>
-zamien NULL na nullptr
-uzywaj range for
-kiedy konstruktor private? dla niekopiowalnych, nie tworzy obiektu (klasa ze statycznymi elementami) , nie dziedzicznych, Singleton
ale generalnie uzywaj =delete zamiast wrzucać w obszar prywatny
-wymuszenie uzycia konstruktora =default  przeciwnienstwo =delete

STL:
-std::string:  (zamiast tablicy char w stylu C)
   sam zarządza pamięcią
   ma wbudowane funkcje
   przeladowane operatory
   kontener wiec działa z algorytmami
   konwersje
   move semantics

std::vector
   te same zalety co wyżej
   zajmuje ciągły obszar pamięci
   Rozważ std::array for  fixed-size collections

Zamiana starych tablic C na kontenery c++, kroki:
-zacznij tam gdzie tablica char* or [] jest tworzona
-zmien typ zmiennej
-zamien size variable na  .size()
-oczyść kod z delete (kontenery same zwalniają pamięć)
-zastąp pętle wykonujące kod na tablicach, algorytmami
-iteruj Range forem
-zmien funkcje w których przekazywałeś tablice jako argument (rozważ kopie, referencję, const)
-uzyj .c_str()  jeśli potrzebujesz stringa z char*

SmartPointers <memory>
-RAII is great (alocate in constructor - release in destructor, rozważ zachowanie kopi płytka-głęboka)
zastanów się nad lifetime and ownership i wybierz std::unique_ptr  std:share_ptr
sharepointer nie powinien byc pierwszym wyborem, raczej unique  (wymusza argumenty przez &)






Bjarne Stroustrup: C++11 Style - konferencja 

24:15  - przyklad tworzenia jednostek miary
27:22  - keep interfaces strongly typed (avoid general types in interfaces)

Resources and Errors
30:49 RAII - przyklad z otwarciem pliku, uzycie klasy  handle
Resource acquisition is initialization - Przejęcie zasobu jest połączone z konstrukcją, a zwolnienie z automatyczną destrukcją zmiennej
33:34 Resource Handles and Pointers (no naked New!) czysty pointer nie jest
           bezpieczny z rzucaniem wyjatkow, wychodzi z funkcji i nie robi delete.
           uzyj smart ale nie shared_ptr, tylko unique_ptr, wyjdzie z zakresu-skasuje
           po co wogóle pointery a nie lokalne?
           jeśli nie uzywasz danych "na zewnątrz" zrezygnuj całkowicie z pointerów

35:50 Resource Management Style:
-Prefer classes where the resource management is part of their semantics (vextor, thread)
-use "smart pointers" unique for unique ownership, shared for shared
-pamietaj nie uzywaja pointerow jesli nie musisz, pointer to ryzyko race condition!!         

37:54 Move resources - bad example - zastąpione semantyka przenosenia
           (tylko std:array nie moze byc moved, reszta kontenerow moze )

43:21 Style:
-no naked pointers
-keep arrays out of interfaces (prefer containers)
-always consider std::unique_ptr and sometimes std::shared_ptr
-return objects "by value" (its using move not copy - copy elision)
 (no fiddle with pointer,references )

Use compact data:
vector vs List (jeden szybko wstawia, drugi szybko przechodzi do elementu )
ale czemy uzywac kompaktowych (vector)
lista przechowuje dodatkowo wskazniki na poprzedzajacy i nastepny co przy
64 bitowej atchitekturze powoduje ze 100000 elementow w liscie zajmie 6,4MB a vectorze 1,6MB
Pamiętaj dostęp do pamięci jest bardzo wolny, 200 instrukcji na jeden dostęp
im bardziej kompaktowy kontener (mniejszy) tym większa szansa umieszczenia w cache!!!

Prefer algorithm to unstructured code (czesto szybszy, zawsze bezpieczniejszy)
Stay high level:
-for_each alghorithm create the same code than  for loop
-c++ sort() is fastes than C  qsort.

Dziedziczenie:
-uzywaj tylko kiedy masz hierarchiczną strukturę
-nie naduzywaj tego

Watki:
1:05:25 - przyklad watków z async i auto

reguła 0:
korzystaj z deflautowych konstruktorów, jesli napiszesz destruktor dopisz reszte z deflaut.

20 C++17/20


C++ 17
Karta Features of c++17 (link)

Karta Features of c++ 20(link)

Handy C++17 Features (link)

std::optional 

std::variant

if constexpr

C++20 art LINK

C++20 Filmik cppcon2020


.......................................
When you see an article about new C++ features, most of the time you’ll see a description of major elements. Looking at C++17, there are a lot of posts (including articles from this blog) about structured bindings, filesystem, parallel algorithms, if constexpr, std::optional, std::variant… and other prominent C++17 additions.

In this article, I’ll show you 17 smaller C++17 things that will improve your code.

The Language
1. Dynamic Memory Allocation for Over-Aligned Data
2. Inline Variables
3. __has_include Preprocessor Expression
The Standard Library
4. Variable Templates for Traits
5. Logical Operation Metafunctions
6. std::void_t Transformation Trait
7. std::from_chars
8. Splicing for maps and sets
9. try_emplace() Method
10. insert_or_assign() Method
11. Return Type of Emplace Methods
12. Sampling Algorithms
13. gcd(), lcm() and clamp()
14. Shared Pointers and Arrays
15. std::scoped_lock
Removed Elements
16. Removing auto_ptr
17. Removing Old functional Stuff
.........................................



std::optional 

Std::optional dodaje nam możliwość zwrócenia stanu "pusty" - nullopt przy różnych typach.
Stan "pusty" - nullopt, może być sprawdzany warunkiem IF jak w przykładzie:

Przykład działania optional


#include <iostream>
#include <vector>
#include <optional>

void Process(int) { };

int main()
{
    std::vector<int> vec { 1, 2, 3, 4, 5, 6, 7, 101, 8, 9 };
    std::optional<int> largerThan100;
    
    for (auto& elem : vec)
    {
        Process(elem);
        
        if (elem > 100 && !largerThan100)
            largerThan100 = elem;
    }
    
    // later on selected elements...
    if (largerThan100)
        std::cout << *largerThan100;
}



Można również zwrócić taki stan za pomocą return std::nullopt  jak w poniższym przykładzie:
Drugi przykład działania option

#include <iostream>
#include <vector>
#include <optional>

template <typename TContainer>
std::optional<typename TContainer::value_type> FindVal(const TContainer& container, const typename TContainer::value_type& key)
{
 auto it = std::find(std::begin(container), std::end(container), key);
 if (it != std::end(container))
  return *it;

 return std::nullopt; // or std::nullopt
}

int main()
{
 std::vector<int> v{ 1, 2, 3, 4, 5, 6, 7 };
 auto i = FindVal(v, 1);
 if (i)
  std::cout << *i;
}




std::variant

Std::variant może przyjmować dowolne, wcześniej określone typy, slbo stan pusty (tu realizowany za pomocą std::monostate )

#include <iostream>
#include <vector>
#include <optional>
#include <variant>
#include <cmath>

// aX^2 + bX + c
using TRoots = std::variant<std::monostate, double, std::pair<double, double>>;

TRoots FindRoots(double a, double b, double c)
{
    const auto delta = b*b-4*a*c;

    if (delta > 0.0)
    {
        auto p = sqrt(delta);
        double x1 = (-b + p)/(2*a);
        double x2 = (-b - p)/(2*a);
        return std::pair(x1, x2);
    }
    else if (delta == 0.0)
    {
        return -b/(2*a);
    }
    
    return std::monostate();
}

void ShowRoots(const TRoots& roots)
{
    switch (roots.index())
    {
        case 0: std::cout << "no roots!\n"; break;
        case 1: std::cout << std::get<1>(roots) << '\n'; break;
        case 2: std::cout << std::get<2>(roots).first << ", " << std::get<2>(roots).second << '\n'; break;
    }
}

int main()
{
    ShowRoots(FindRoots(1, -3, 4));
    ShowRoots(FindRoots(-4, 12, -9));
    ShowRoots(FindRoots(2, -11, 5));
}

Patrz na przykład zastosowania std::variant jako nowoczesnej maszyny stanów.
LINK





if constexpr

Pozwala wybierać gałęzie na poziomie kompilowania  na podstawie warunków constexpr (wyrażenie stałe).



template <typename T>
auto get_value(T t) {
  if constexpr (std::is_pointer_v<T>)
    return *t;
  else
    return t;
}

Uproszcza kod Templatów, szczególnie jeśli używane SFINAE
 (Niepowodzenie podstawienia nie jest błędem (SFINAE) odnosi się do sytuacji w C ++, w której nieprawidłowe zastąpienie parametrów szablonu samo w sobie nie jest błędem)


wiecej 303 i 58 (książka Filipek)




19 Wzorce Projektowe

SOLID (R.C. Martin)
(założenia programowania obiektowego)

Single responsibility - Klasa powinna mieć tylko jedną odpowiedzialność
Open - Klasy (encje) powinny być otwarte na rozszerzenia i zamknięte na modyfikacje.
Liskov podstawianie - Funkcje z wskaźnikami (referen) do klas bazowych, muszą być w stanie używać również obiektów klas dziedziczących po klasach bazowych (bez ich znajomości) LINK
Interface segregation - Wiele dedykowanych interfejsów jest lepsze niż jeden ogólny
Dependency inversion - Wysokopoziomowe moduły nie powinny zależeć od modułów niskopoziomowych. Tworzymy interfejsy których używają wysokopoziomowe i wstrzykujemy wysokim w konstruktorze adres obiektu niskiego poziomu. Dzięki czemu wysoki nie jest zależna od konkretnego niskiego ale zależy od abstrakcji (interfejs). Abstrakcja reprezentowana przez zmienna składową inicjalizowaną konstruktorem.



Books:
Gang of Four - in software development 



UML soft:
Darmowe narzędzie online do tworzenia diagramów
UML z zastosowaniem plantuml



WZORCE:

0 Wstrzykiwanie zależności (dependency injection ) - nie opisana w Gang of Four.


1 Konstrukcyjne

2 Strukturalne

3 Operacyjne (czynnościowe )


4 Architectural Patterns

Anti-Patterns

Okreslenia

7 Czysty Kod



1
                                                        Creational patterns    - Konstrukcyjne (tworzenie)

Singleton s130 *   kod
gwarantuje, że klasa będzie miała tylko jeden egzemplarz i zapewnia globalny dostęp do jej obiektu             

Factory Method  s110 * (czysty kod s 250)     kod
określa interface do tworzenia obiektów, umozliwia klasom przekazanie tworzenia egzemplarzy PODLKASOM

Abstract Factory  s101 
udostepnia interface do tworzenia rodzin powiazanych ze soba obiektów bez okreslania ich klas konkretnych     

Builder s92 *    kod 
oddziela tworzenie obiektu od jego reprezentacji




2
                                                 Structural Patterns      -strukturalne   (relacje )

Adapter s141 * (czysty kod s 231)  kod 
przeksztalca interface klasy na oczekiwany przez klienta, umozliwia wspoldzialanie klasom, które nie moga

Decorator s152
dolacza dodatkowe obowiazki do obiektu (tworzenie podklas o wzbogaconych funkcjach)

Bridge s181
oddziela abstrakcję od jej implementacji (mozna je modyfikowac niezaleznie)

Facade s161 * (czysty kod s252)    kod 
udostępnia jednolity interface dla zbioru interfejsów z podsystemu. Fasada określa interfejs wyższego poziomu ułatwiając korzystanie z podsystemów.

Flyweight s201 (pyłek) ?
Wykorzystuje współdzielenie do wydajnej obsługi dużej liczby małych obiektów.

Composite s170  (czysty kod s 242)
Składa obiekty w struktury drzewiaste odzwierciedlające hierarchię typu część-całość. Wzorzec ten umożliwia klientom traktowanie poszczególnych obiektów i ich założeń w ten sam sposób.



3
                                               Behavioral Patterns        -operacyjne(czynnościowe ):

Chain of Responsibility s244 
pozwala unikac nadawcy wiazania go z odbiorca poniewaz umozliwia obsluzenie zadania wiecej niz jednemu obiektowi

Template Method s264
okresla szkielet algorytmu i pozostawia doprecyzowanie niektorych jego krokow podklasom

Observer s269 * (czysty kod s 245)  kod 
okresla zależność "jeden do wieli" między obiektami, kiedy zmieni sie stan jednego, reszta wie

Strategy s321 (czysty kod s 233)
okresla rodzine algorytmow, kapsulkuje kazdy z nich i umozliwia ich zamienne stosowanie

Command (polecenie ) s302 (czysty kod s237)
Kapsułkuje żądanie w formie obiektu. Umożliwia to parametryzację klienta przy użyciu różnych żądań oraz umieszczanie żądań w kolejnych dziennikach, a także zapewnia obsługę cofania operacji.



4
 Architectural Patterns 
LINK
Layered pattern
Client-server pattern
Master-slave pattern
Pipe-filter pattern
Broker pattern
Peer-to-peer pattern
Event-bus pattern
Model-view-controller pattern
Blackboard pattern
Interpreter pattern


5
                                                                     -Anti-Patterns: 

Singleton - wszystkie klasy które z niego korzystają mają bezpośrednie odwołanie do niego. Obiekt singleton w programowaniu obiektowym jest jak  zmienna globalnia w programowaniu proceduralnym.
Możesz używać tego obiektu w dowolnym miejscu, ale jest to widoczne tylko w miejscu użycia a nie w interfejsie!!



6

Okreslenia:
delegacja - dostarczanie przez argument
asocjacje:
agregacja - obiekt posiada pole z innym obiektem (składaSieZ, na UML pusty romb i czarna strzałka)
kompozycja - silna asocjacja, całość posiada część i odpowiada za nią, gdy usuwamy całość, części też są usuwane (zawiera, na UML pełny romb i czarna strzałka)
.
funkcja wyższego poziomu - przyjmuje jedną lub więcej funkcji jako argumenty lub zwraca funkcję.
KISS (zasada projektowa)- keep it simple stupid
DRY(zasada projektowa)- don't repeat yourself




7

Czysty Kod C++17:

Nazwy          (oczywiste,bez zagadkowych skrótów, bez węgierskiej, z dziedziny)

Komentarze (bez oczywistych, bez blokowych, nie dezaktywuj kodu komentarzem)

Funkcje:        
-pojedyncze zadanie
-niewielkie
-max 3 argumenty ale w proceduralnym może być więcej
-unikaj argumentów typów złożonych (klasy - podnosi zależność )
-unikaj argumentów w postaci flag (2 ścieżki funkcji) lepiej 2 funkcje
-unikaj parametrów wyjściowych (czyli argumentów przez referencje które funkcja zmienia, chaotyczne, lepiej zwracać, jeśli funkcje zwracają tylko jedną wartość można łatwo łączyć je w łańcuchy wywołań, jeśli musisz zwrócić kilka wartości zwróć std::tuple lub std::pair
-nie zwracaj NULL ani nullptr jesli funkcja zwraca zwykły wskaźnik, przerzucasz na jednostkę wywołującą obsługę pojawienia się takiej sytuacji, nie jest też jasne co się stało, czy nie znalazł, czy błąd itp...
-unikaj zwykłych wskaźników, stos nie sterta, argument przez referencję nie wskaźnik, jeśli musisz użyć wskaźników użyj smartptr
-używaj const jak najczęściej w celu ochrony danych

C a C++       (nowoczesne zamiast char*, nie uzywaj printf itp, kontenery stl nie tablice, stosuj rzutowanie c++, unikaj MAKR)

C++ zaawansowane: 
-stosuje smartptr
-unikaj new i delete - alokuj na stosie lub std::make, korzystaj z kontenerów które zarządzają pamięcią
-używaj semantyki przenoszenia r-value ale nie wszędzie RVO copy-elision
-stosuj regułę zera!
-auto - automatyczna dedukcja typu
-obliczenia na etapie kompilacji czyli constexpr(wyrażenie stałe)
-używaj algorithm



Obiektowe    (SOLID, unikaj statycznych składowych klas)

RAII (ang. Resource Acquisition Is Initialization) - popularny wzorzec projektowy, którego zadaniem jest przejęcie kontroli nad zasobem w chwili tworzenia obiektu (w konstruktorze) i zagwarantowanie, że zasób zostanie automatycznie zwolniony (w destruktorze).

TDD               (zamiast dopisywania testów jednostkowych pisz TDD, zadanie TDD LINK)

Wzorce projektowe (używaj :-)

artykuł w Programista:
Czysty kod w embedded                Programista nr 78 str36


                                                                             KOD:

0 Wstrzykiwanie zależności (dependency injection ) 

Zamiast tworzyć singletony, utwórz tylko jeden obiekt i wstrzykuj go tam gdzie jest potrzebny.
Obiekty kliencie  nie muszą samodzielnie zarzącać potrzebnymi obiektami ani rządać ich od np fabryki.
Cel:
Oddzielenie komponentów od wymaganych przez nie usług, tak by komponenty nie musiały znać nazwy tych usług ani sposobu ich zapewniania.
Wykorzystanie przy stosowaniu jednego z założen programowania obiektowego, czyli odwróconej zależności, dependency inversion - wysokopoziomowe moduły nie powinny zależeć od modułów niskopoziomowych. Dlatego Tworzymy interfejsy których używają wysokopoziomowe obiekty i wstrzykujemy im w konstruktorze adres obiektu którym steruje ten interface (które dziedziczą po interfejsie). Dzięki czemu wysokopoziomowy obiekt nie jest zależny od konkretnej instancji obiektu niskiego poziomu ale zależy od abstrakcji (interfejs). Abstrakcja reprezentowana przez zmienna składową inicjalizowaną konstruktorem.

Wzrasta również łatwość testowania, nie występują ukryte zależności od singletonów. Teraz można łatwo zastapić rzeczywistą usługę atrapą. 
Dobry obiektowy projekt powinien gwarantować, że używana moduły są możliwie luźno powiązane a wstrzykiwanie zależności to droga do tego. 

Odpowiedzialność za tworzenie obiektu i konsolidowanie go z innymi jest przenoszona z obiektów do scentralizowanego komponentu infrastrukturalnego - assemblera który działa na poziomie rozruchu programu. 




Strzałki pełne, asocjacje idą z dołu do góry, czyli moduł wyższego rzędu CustomerRepository nie zależy od niższych. Wyższa korzysta z LogingFacility (interface) po którym z kolei dziedziczą  obiekty niższego rzędu (strzałka pusta - Generalizacja/dziedziczenie) Standard i File Logger.
Relacja pomiędzy modułem wyższego rzędu a interfejstem (pełny romb i czarna strzałka ) to kompozycjasilna agregacja, moduł wyższy posiada pole z adresem obiektu (typu interface, inicjalizowane w konstruktorze) i odpowiada za nią .

Jeśli nie ma możliwości wstrzykiwania za pomocą konstruktora, obiekt musi udostępniac setter obiektu usługowego



1 Konstrukcyjne

Singleton s130 (konstrukcyjny)
Klasa z implementacją singletona zawiera:
-statyczną zmienną przechowującą instancję tej klasy,
-prywatny „pusty” konstruktor (aby nie można było utworzyć nowego obiektu tej klasy z wykorzystaniem operatora „new”),

-publiczną statyczną metodę „getInstance” bez żadnych parametrów.

class Singleton {
    static Singleton instance;
    Singleton() {}
public:
    static Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
            }
        return instance;
        }
};
.


Factory (konstrukcyjny)
Za każdym razem gdy używamy w kodzie operatora new, uzależniamy się od konkretnej implementacji zamiast od interfejsu. Jedna z zasad programowania obiektowego (reguła odwracania zależności / dependency inversion principle) mówi:
Uzależniaj kod od abstrakcji, a nie od klas rzeczywistych.
druga zasada open/closed mówi:
System powinien być otwarty na rozbudowę, ale zamknięty na modyfikacje.



Simple Factory
Przeważnie wywoływana przez klienta przez statyczną metodę (przeważnie parametryzowaną) i zwraca jeden z wielu obiektów tego samego interfejsu.

jeśli mamy w klasie część kodu która w przyszłości może być rozbudowywana, np tworzenie roznych
wersji nowych obiektow na podstawie roznych argumentow, to przenosimy ten fragment kodu do osobnej klasy:

class SimpleFactory {
public:
Product createProduct(String type) {
     if (type.equals("round")) {
         Product product = new RoundProduct();
         } 
            else if (type.equals("square")) {
         Product product = new SquareProduct();
         }
     return product;
 } 
};
.

Teraz pierwotna klasa zawierająca tą metodę będzie wyglądać następująco:

class ProductProcess {
SimpleFactory factory;

public:
ProductProcess(SimpleFactory factory) {      //konstruktor, ale mozna by tworzyc fabryke samodzielnie
  this.factory = factory; }    //tyle, ze ty mozemy podac zmodyfikowana
 
void doAction(String type) {
  Product product = factory.createProduct(type);
...
 }
};
.
Prosta fabryka wywoływana jest bezpośrednio przez klasę która chce utworzyć nowy obiekt. Wywołana metoda fabryki zwraca jeden z wielu obiektów dziedziczących po wspólnej klasie bazowej lub implementujących wspólny interfejs.


Factory Method s110
Wykorzystuje mechanizm dziedziczenia. Implementacja polega na stworzeniu metody abstrakcyjnej która zostanie zaimplementowana w klasie po niej dziedziczącej.

Wzorzec ten definiuje dwie równoległe hierarchie klas (interfejsy):
-Klasy produktów (np. RoundProduct, SquareProduct).
-Klasy fabryki (np. PolishFactory, GermanFactory).

Metoda fabrykująca definiuje  interfejs pozwalający na tworzenie obiektów, równocześnie pozwalając klasom podrzędnym decydować jakiej klasy obiekt zostanie stworzony. Odpowiedzialność za tworzenie obiektów zostaje przekazana klasom podrzędnym.

Implementacja wzorca dla napisanej wcześniej klasy ProductProcess kozystajacej z fabryki
może wyglądać tak:

class AbstractProductProcess {
public:
void doAction(String type) {
  Product product = createProduct(type);
 }
 
protected:
virtual Product createProduct(String type)=0;
};
.

NIE MA TUTAJ OSOBNEJ KLASY FABRYKI Metodą fabrykującą jest abstrakcyjna metoda:
virtual Product createProduct(type);

ktora nadpiszemy  i stworzymy fabryke!:

Załóżmy, że chcemy teraz przygotować możliwość przetwarzania produktów w dwóch wersjach:
Wersja dla lokalizacji w Polsce.
Wersja dla lokalizacji w Niemczech.

polska fabryka:
class PolishProductProcess: public AbstractProductProcess {
protected: 
createProduct(String type) override {

     if (type.equals("round")) {
         Product product = new PolishRoundProduct();
     } 
            else if (type.equals("square")) {
         Product product = new PolishSquareProduct();
     }
 return product;
 }
};
.

niemiecka fabryka:
class GermanProductProcess: public AbstractProductProcess {
protected: 
createProduct(String type) override {

     if (type.equals("round")) {
         Product product = new GermanRoundProduct();
     } else if (type.equals("square")) {
         Product product = new GermanSquareProduct();
     }
 return product;
 }
};
.

przykład uzycia:
AbstractProductProcess factory;
factory = new PolishProductProcess();
factory.doAction("round"); //zostaną wykonane operacje na utworzonym obiekcie PolishRoundProduct
 
factory = new GermanProductProcess();
factory.doAction("square"); //zostaną wykonane operacje na utworzonym obiekcie GermanSquareProduct
.
Metoda fabrykująca pozwala klientom podrzędnym decydować jakiej klasy obiekt zostanie utworzony.


Abstract Factory s101 (konstrukcyjny)
Wykorzystuje kompozycję. Zwraca całą rodzinę powiązanych ze sobą obiektów.
Ideą fabryki abstrakcyjnej jest tworzenie rodzin spokrewnionych ze sobą obiektów. Ten wzorzec tak jak poprzednie pozwalają na tworzenie kodu, który będzie uzależniony od abstrakcji, a nie od implementacji konkretnych klas.

warstwowo pierwsza fabryka komponenty typu pierwszego, druga drugiego
kolejna warstwa fabryka korzystajaca z pierwszej warstwy
przykład:
link


Builder s92 (konstrukcyjny)
Należy rozważyć użycie wzorca budowniczy gdy:
-obiekt, który tworzymy jest złożony i nie da się go uprościć
-nie da się utworzyć instancji obiektu poprzez jednorazową operację (wieloetapowa inicjalizacja)
-obiekt, którzy tworzymy, będzie budowany wiele razy w różny sposób

słożony z:
-budowniczy (CarBuilder) – dostarcza abstrakcyjny interfejs służący do budowania finalnego produktu
-konkretny budowniczy (SmallCarBuilder) – dostarcza implementację metodom budowniczego
-kierownik (CarDirector) – konstruuje obiekt z wykorzystaniem jakiegoś budowniczego (logika)
-produkt (Product) – finalny złożony obiekt, dostarczony przez kierownika, zbudowany za pomocą budowniczego

// budowniczy - interfejs implementacji (abstrakcyjna klasa)
class CarBuilder
{
Car* car;                           //zmienna gdzie bedzie tyworzony samochod
public:
    virtual void BuildWheels()=0;
    virtual void BuildAddons()=0;
    virtual void BuildEngine()=0;

    Car* GetResult
    {
        return this.car; 
    }
};
.

// konkretny budowniczy - implementacja
class SmallCarBuilder : CarBuilder
{
public:
    SmallCarBuilder()  //konstruktor
    {
        this.car = new Car();
    }

    void BuildWheels() override
    {
        car.Wheels = "Steel rims 15 inches";
    }

    void BuildAddons() override
    {
        car.Addons = new List<string>();
        car.Addons.Add("CD radio with MP3");
        car.Addons.Add("CD radio");
    }

    void BuildEngine() override
    {
        car.Engine = EngineFactory.CreateEngine("120 HP engine");
    }
};
.

// kierownik - logika inicjalizacji obiektu
class CarDirector
{
public:
 void ConstructCar(CarBuilder builder)
    {
        builder.BuildWheels();
        builder.BuildAddons();
        builder.BuildEngine();
    }
};
.

tworzenie:
void Main(string[] args)
{
    CarDirector carDirector = new CarDirector();           //kierownik
    CarBuilder smallCarBuilder = new SmallCarBuilder();    //konkretny budowniczy

    // buduj według logiki kierownika używając implementacji z konkretnego budowniczego
    carDirector.ConstructCar(smallCarBuilder);

    Car* smallCar = smallCarBuilder.GetResult;     //przypisz produkt do zmiennej
}
.
  //mozliwa wersja, getresult w kierowniku zamiast w budowniczym,

Opis:
najważniejszymi elementami wzorca jest kierownik oraz konkretny budowniczy. Kierownik skupia logikę budowania, konkretny budowniczy skupia implementację tej logikii
budowniczy jest nic nieznaczącym interfejsem (w przykładzie klasa abstrakcyjna) aby zapewnić wspólny interfejs polimorficzny dla konkretnych implementacji budowniczych (spełnienie 4 zasady SOLID odwrócenie zależności)
dzięki oddzieleniu logiki od implementacji, możemy zmienić logikę budowania obiektu w jednym miejscu (kierownik) i zostanie ona zmieniona w całym systemie – w każdym budowniczym.




2 Strukturalne

Adapter s141 * (czysty kod s 231)
Przeksztalca interface klasy na oczekiwany przez klienta. Bardzo często stosowany, dostosowuje niezgodne interfejsy.
Dodajemy klasę dziedziczącą po interfejsie (adapter który dziedziczy po interfejsie a potrafi obsługiwać nasz niekompatybilny objekt). Tak naprawdę adapter to nowa implementacja interfejsu.
Jeśli interfejsy bardzo się różnią komplikuje to adapter.




.

#include<iostream>
#include<string>

using namespace std;

class IPlayer //Client wants to use this.
{
public:
 virtual void Play() = 0; //This is function client wants to use
};

class OldPlayer //What we have
{
 string _song;
public:
 OldPlayer(string song) :_song(song){}

 void OldPlay(int volume) //This is function we got
 {
  cout << "\nPlaying song : " << _song << " at volume :" << volume << "\n";
 }
};

class Adapter : public IPlayer, public OldPlayer
{
 int _volume;
public:
 Adapter(string song, int volume) :_volume(volume), OldPlayer(song){}

 void Play()  //Comes from public inheritance
 {
  OldPlay(_volume); //Comes from private inheritance
 }
};

int main()
{
 IPlayer* newStuffDad = new Adapter("Good Ole Boy Like Me", 50);
 newStuffDad->Play(); //The call to the interface function is routed by the adapter to the LegacyCode.
 cout << "\n";
        delete newStuffDad;
 return 0;
}






Facade s161 * (czysty kod s252) strukturalny

Zapewnienie jednolitego interfejsu dla zbioru interfejsow podsystemu. Definiuje interface wyższego poziomu.

Tworzenie struktury dużego systemu prowadzi do powstania większych komponentów i modułów. Należy ukryć przed klientami wewnętrzną strukturę. Niewskazane by klient podsystemu mógł znać jego wewnętrzną strukturę. Wzorzec fasada reguluje dostęp do złożonego systemu, zapewniając prosty interface. Dostęp wyłącznie za jego pomocą.





#include <iostream>

// Subsystem 1
class SubSystemOne
{
public:
 void MethodOne(){ std::cout << "SubSystem 1" << std::endl; };
};

// Subsystem 2
class SubSystemTwo
{
public:
 void MethodTwo(){ std::cout << "SubSystem 2" << std::endl; };
};

// Subsystem 3
class SubSystemThree
{
public:
 void MethodThree(){ std::cout << "SubSystem 3" << std::endl; }
};


// Facade
class Facade
{
public:
    Facade()
    {
 pOne = new SubSystemOne();
 pTwo = new SubSystemTwo();
 pThree = new SubSystemThree();
    }

    void MethodA()
    {
 std::cout << "Facade::MethodA" << std::endl;
 pOne->MethodOne();
 pTwo->MethodTwo();
    }

    void MethodB()
    {
 std::cout << "Facade::MethodB" << std::endl;
 pTwo->MethodTwo();
 pThree->MethodThree();
    }

private:
    SubSystemOne *pOne;
    SubSystemTwo *pTwo;
    SubSystemThree *pThree;
};

int main()
{
    Facade *pFacade = new Facade();

    pFacade->MethodA();
    pFacade->MethodB();

    return 0;
}








 3 Behavioral Patterns        -operacyjne(czynnościowe ):


Observer s269 * (czysty kod s 245)

Definiowanie zależności jeden do wielu między obiektami w taki sposób, że gdy jeden obiekt
zmienia stan, wszystkie zależne obiekty są powiadamiane i automatycznie aktualizowane.

Pomyśl o arkuszu kalkulacyjnym, który jest typowym składnikiem wielu pakietów oprogramowania biurowego. W takiej aplikacji dane mogą być wyświetlane w arkuszu roboczym, na wykresie kołowym i w wielu innych formach. Są to widoki, które można tworzyć, a następnie ponownie zamykać.
Przede wszystkim potrzebna jest klasa abstrakcyjna reprezentująca widoki — Observer.
Obserwatory obserwują podmiot. Na potrzeby obserwacji można je zarejestrować w podmiocie.
Możliwe jest też wyrejestrowanie obserwatora.

Najważniejsza jest tu funkcja składowa notifyAllObservers(). Opatrzona jest ona specyfikatorem
protected, ponieważ ma być wywoływana przez klasy konkretne z rodziny Subject dziedziczące po danej klasie. Ta funkcja pobiera wszystkie zarejestrowane obserwatory i wywołuje ich funkcję składową update().

Oprócz korzystnego luźnego powiązania (klasa konkretna podmiotu nie wie nic na temat obserwatorów) ten wzorzec dobrze wspomaga też zasadę otwarte – zamknięte. Teraz można łatwo dodawać klasy konkretne obserwatorów.

Obserwatorzy dziedziczą po jednej klasie, dzięki czemu kontener je rejestrujący może być jednego typu.


Zastosowanie: W MVC  chodzi m.in. o to, by oddzielić przechowywane w modelu wyświetlane dane od różnych wizualnych reprezentacji tych danych, czyli widoków.
We wzorcu MVC powiązanie między widokami a modelem powinno być tak luźne jak to możliwe.
To luźne powiązanie jest zwykle realizowane za pomocą wzorca obserwator.
.

#include <vector>
#include <iostream>

using namespace std;

class Observer
{
  public:
    virtual void update(int value) = 0;
};

class Subject
{
    int m_value;
    vector<Observer *> m_views;
  public:
    void attach(Observer *obs)
    {
        m_views.push_back(obs);
    }
    void set_val(int value)
    {
        m_value = value;
        notify();
    }
    void notify()
    {
        for (int i = 0; i < m_views.size(); ++i)
          m_views[i]->update(m_value);
    }
};

class DivObserver: public Observer
{
    int m_div;
  public:
    DivObserver(Subject *model, int div)
    {
        model->attach(this);
        m_div = div;
    }
     /* virtual */void update(int v)
    {
        cout << v << " div " << m_div << " is " << v / m_div << '\n';
    }
};

class ModObserver: public Observer
{
    int m_mod;
  public:
    ModObserver(Subject *model, int mod)
    {
        model->attach(this);
        m_mod = mod;
    }
     /* virtual */void update(int v)
    {
        cout << v << " mod " << m_mod << " is " << v % m_mod << '\n';
    }
};

int main()
{
  Subject subj;
  DivObserver divObs1(&subj, 4);
  DivObserver divObs2(&subj, 3);
  ModObserver modObs3(&subj, 3);
  subj.set_val(14);
}


Przykładowy Kod innych wzorców projektowych



Wzorce skrótowo:


Adapter:
dostosowuje niezgodne interfejsy
jeśli mamy dwa interfejsy tworzymy klasę dziedziczącą po obu i tworzymy metodę która łączy i wywołuje oba interfejsu.




Facory:
bazowa: Car  (produkt)
dziedziczą: Fiat, Mercedes

bazowa:  AbstractProductProcess   (fabryka)
która ma 2 metody:
void doAction(String type) {
  Product product = createProduct(type);
 }
i virtualną (nadpisywaną w konkretnej fabryce)
virtual Product createProduct(String type)=0;
dziedziczy:
PolishProductProcess
(nadpisuje virtualną createProduct)
if (typ1)
  Car* car1=new Fiat();
if else (typ2)
  Car* car2=new Polonez();
itp:




Fasada:
Zapewnienie jednolitego interfejsu dla zbioru interfejsow podsystemu
większe moduły (zbiory obiektów) ukryte i tylko stworzona fasada(obiekt) do komunikacji z nimi:

class Facade
{
konstruktor (albo tworzy obiekty/instancje interfacow z modułu, albo sa wstrzykiwane)

Metoda1dlaKlienta(uruchamia ukryte metody powyzszych obiektow)

Metoda2dlaKlienta(uruchamia inne ukryte metody powyzszych obiektow)
}




Observer:
jeden do wielu między obiektami w taki sposób, że gdy jeden obiekt zmienia stan,
wszystkie zależne obiekty(obserwatorzy) są powiadamiane i automatycznie aktualizowane.

Obserwatorzy dziedziczą po jednej klasie(Obserwer), dzięki czemu kontener w Subject je rejestrujący może być jednego typu.
Observer interface ma jedną metodę visrtualną, update, którą uruchamia Subject jak chce poinformować.

klasa konkretna Subject nie wie nic na temat obserwatorów ten wzorzec dobrze wspomaga wiec zasadę otwarte na rozszerzanie– zamknięte na modyfikacje

Subject ma:
-kontener na obserwery
-dodaj (*observer)
dodaje do kontenera observery
-notyfy
iteruje po kontenerze i uruchamia update na observerach


Budowniczy:

słożony z:
-budowniczy (CarBuilder) – abstrakcyjny interfejs do budowania finalnego produktu
-konkretny budowniczy (SmallCarBuilder) – implementacja budowniczego
-kierownik (CarDirector) – konstruuje obiekt/uruchamia metody budowniczego
-produkt (Product) – Car, tworzony przez kirownika z wykorzystaniem budowniczego


class Car                                                                             (produkt)

class Budownioczy{                                                           (interface z metodami budowania)
virtual stworzNoweAuto()
virtual zrobKoła();
virtual zróbSilnik();

class KonkretnyBudowniczy : Budowniczy {                   (implementacja metod budowania )
stworzNoweAuto override
zrobKola ovveride
zróbSilnik ovveride

class Kierownik                                                                     (logika, kolejność uruchamiania metod )
Car stworzauto(budowniczy)                                                 (uruchamiane w metodzie stwórzauto)
{
stworzNoweAuto
uruchom robKola
uruchom zróbSilnik
get result {zwróc stworzone auto Car}
}


main()

Budownioczy* budowniczy = new KonkretnyBudowniczy1
Kierownik * kierownik = new Kierownik ()

Car autko1 = kierownik->stworzNoweAuto(budowniczy)


Kierownik skupia logikę budowania, konkretny budowniczy skupia implementację tej logikii
jeden kierownik, kulka rodzaji budowniczych.

przykład budowniczego z shared_ptr

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

struct Car
{
    int maxspeed;
    int wielkosc;
};

struct AbstractBuilder
{
    std::shared_ptr<Car> tempauto;

    virtual void predkosc()=0;
    virtual void rozmiar()=0;
    virtual void create()=0;
    virtual std::shared_ptr<Car> zwroc()=0;
};

struct Builder1 : AbstractBuilder
{

   void create()
    {
        tempauto.reset(new Car());
    }

    void predkosc()override
    {
        tempauto->maxspeed=1;
    }

    void rozmiar()override
    {
        tempauto->wielkosc=1;
    }

    std::shared_ptr<Car> zwroc()
    {
        return std::move(tempauto);
    }
};


struct Kierownik
{
    std::shared_ptr<Car> makeCar(AbstractBuilder* obj)
    {
    obj->create();
    obj->predkosc();
    obj->rozmiar();
    return obj->zwroc();
    }
};


int main ()
{
AbstractBuilder* builder1=new Builder1();
Kierownik kierownik;

std::shared_ptr<Car> auto1a=kierownik.makeCar(builder1);
std::shared_ptr<Car> auto1b=kierownik.makeCar(builder1);

std::cout<<auto1a->maxspeed<<" "<<auto1a->wielkosc<<std::endl;
std::cout<<auto1b->maxspeed<<" "<<auto1b->wielkosc<<std::endl;

// wypisz wewnętrzne dane wskaźnika auto1a
cout << "auto1a use_count: " << auto1a.use_count () << endl;

// wypisz wewnętrzne dane wskaźnika auto1b
cout << "auto1b use_count: " << auto1b.use_count () << endl;

return 0;
}




http://hilite.me/
monokai