SOLID (R.C. Martin)
(założenia programowania obiektowego)
UML soft:
Darmowe narzędzie online do tworzenia diagramów
UML z zastosowaniem plantuml
WZORCE:
(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
(Wzorce projektowe - obiektowe oprogramowanie wielokrotnego użytku )
Zostań architektem oprogramowania
PWN Link
Zostań architektem oprogramowania
PWN Link
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
5 Anti-Patterns
6 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
LINKLayered 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 argumentasocjacje:
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:
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 kompozycja - silna 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 interfejsyjeś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 podsystemuwię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








