======Wzorzec Most====== Wzorzec Mostu (ang. //Bridge pattern//) jest wzorcem strukturalnym. Wzorzec oddziela abstrakcję od jej implementacji. Pozwala to na usunięcie powiązań pomiędzy implementacją a obiektami które ją wykorzystują, co powoduje, że abstrakcja i implementacja mogą zmieniać się niezależnie od siebie. ====Zastosowanie==== * Ukrycie implementacji przed klientem. Metody abstrakcji wywołują metody dostępne przez interfejs implementacji. Tworząc nową klasę pochodną Abstrakcji nie odwołujemy się bezpośrednio do implementacji, a jedynie do metod klasy bazowej abstrakcji, które stanowią opakowanie dla metod implementacji. * Abstrakcje i implementacje można łatwo rozbudować poprzez dodanie nowych klas pochodnych. * Abstrakcja nie jest powiązana na stałe z konkretna implementacją, wiązanie jest dokonywane w momencie tworzenia nowego obiektu, może być również zmieniane w po utworzeniu obiektu. * Wiele obiektów może współdzielić implementację. Abstrakcja przechowuje wskazanie na klasę bazową implementacji, co umożliwia wykorzystanie jednej implementacji przez wiele obiektów. ====Struktura==== Zdefiniowany jest wspólny interfejs dla wszystkich konkretnych implementacji (ImplementacjaInter), który jest wykorzystywany przez klasę abstrakcyjną Abstrakcja. Abstrakcja przekazuje polecenia klienta do obiektu implementacji. {{ diagrammost.jpeg |}} ====Most umożliwia:==== * oddzielenie interfejsu od implementacji * łatwą rozbudowa klas (abstrakcji i interfejsu) * ukrycie szczegółów implementacji przed klientem, co pozwala na zmianę implementacji bez konieczności zmiany interfejsu ====Przykład zastosowania wzorca==== Prosty program demonstrujący wykorzystanie wzorca Most do rysowania różnych kształtów z wykorzystaniem różnych bibliotek graficznych. Program ma pokazać sposób wykorzystania wzorca, nie działania bibliotek, dlatego rysowanie jest zastąpione drukowaniem tekstu na ekranie z informacją która biblioteka została użyta. /************************************** * GRAZYNA STRYJEWSKA grupa H6ISI * **************************************/ /** Przyklad wykorzystania wzorca strukturalnego MOST. Wzorzec pozwala oddzielic abstrakcje od implementacji. * Przyklad demonstruje rysowanie roznych ksztaltow z wykorzytaniem roznych bibliotek graficznych. * Wykorzystane biblioteki roznia sie interfejsem (np. funkcja rysujaca linie w zaleznosci od biblioteki * przyjmuje albo wspolrzedne punktow poczatkowego i koncowego, albo wspolrzedne punktu poczatkowego * dlugosc oraz nachylenie prostej). Wzorzec most pozwala stworzyc wspolny interfejs dla bibliotek. * Wykorzystanie wzorca most pozwala na latwie roszerzenie hierarchii klas zarowno implementacji jak * i abstrakcji poprzez dodanie nowych klas pochodnych. */ #include #include /** Abstrakcyjna klasa dostarczajaca interfejsu dla bibliotek realizujacych zadane funkcjonalnosci.*/ class ImplementationIterface { public: ImplementationIterface() {} virtual ~ImplementationIterface() {} /** Metoda ktora jest opakowaniem dla bibliotecznych funkcji rysujacych linie. Interfejs wymaga * aby do narysowania linii wystarczylo podac wspolrzedne punktow poczatkowego i koncowego. * Jesli biblioteka nie dostarcza fukcji inne paramtery do rysowania linii w implementacji tej metody * w klasie reprezentujacej konkretna biblioteke obliczane sa odpowiednie wartosci przekazywane * funkcji bibliotecznej do narysowania linii ktorej punkt poczatkowy i koncowy bedzie zgodny * z podanym w metodzie drawLine(). */ virtual void drawLine(int x1, int y1, int x2, int y2) = 0; /** Metoda ktora jest opakowaniem dla bibliotecznych funkcji rysujacych kolo. Interfejs wymaga * aby do narysowania kola wystarczylo podac wspolrzedne srodka oraz promien kola. Jesli biblioteka * dostarcza funkcji rysujacej kolo przyjmujacej inne paramtery w implmentacji * tej metody w klasie reprezentujacej konkretna biblioteke nalezy policzyc odpowiednie dane na * podstawie podanych jako parametry metody drawCircle() i przekazac je fukcji bibliotecznej. */ virtual void drawCircle(int x, int y, int r) = 0; }; /** Konkretna implementacja z wykorzystaniem fukcji dostarczonych przez bibliteke A. Biblioteka * posiada metody rysujace linie od punktu poczatkowego do punktu koncowego oraz funkcje rysujaca * kolo o srodku w podanym punkcie i podanym promieniu, dlatego w implementacji metod interfejsu * nie przeprowadzane sa dodatkowe obliczenia. */ class LibraryA : public ImplementationIterface { public: LibraryA() {} ~LibraryA() {} /** "Rysuje" linie na ekranie.*/ void drawLine(int x1, int y1, int x2, int y2) { std::cout << "\tLibraryA LINE1//" << "p1=(" << x1 << ", " << y1 << "), " << "p2(" << x2 << ", " << y2 << ")" << std::endl; } /** "Rysyje" kolo na ekranie.*/ void drawCircle(int x, int y, int r) { std::cout << "\tLibraryA CIRCLE//" << "O =(" << x << ", " << y << ") " << "r =" << r << std::endl; } }; /** Konkretna implementacja z wykorzystaniem fukcji dostarczonych przez biblioteke A. Biblioteka dostarcza * funkcji rysujacej linie o punkcie poczatkowym, podanej dlugosci i kacie nachylenia, dlatego w implementacji * metody drawLinie nalezy przeprowadzic obliczenia wyznaczajace dlugosc i kat nachylenia prostej i przekazac * te wartosci fukcji bibliotecznej. Podobnie jest z rysowaniem kola, biblioteka nie dostarcza fukcji rysujacej * kolo dostarcza funkcje rysujaca elipse. Fukcja przyjmuje wspolrzedne prostokata w ktory elipsa jest wpisana, * dlatego tu rowniez nalezy dokonac odpowiednich obliczen. */ class LibraryB : public ImplementationIterface { public: LibraryB() {} ~LibraryB() {} /** "Rysuje" linie na ekranie. W metodzie jest obliczana dlugosc linii o podanych punktach poczatkowym * i koncowym, oraz kat nachylenia tej linii, a natstepnie wartosci te przekazywane sa do * fukcji bibliotecznej rysujacej linie. */ void drawLine(int x1, int y1, int x2, int y2) { double lenght = sqrt((double)((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1))); double angle = acos(abs(x2-x1)/lenght); std::cout << "\tLibraryB LINE2//" << "pp =(" << x1 << ", " << y1 << ") " <<"lenght = " << lenght << " angle = " << angle << std::endl; } /** "Rysyje" kolo na ekranie. W metodzie obliczane sa wspolrzedne prostokata w ktory wpisane jest * kolo, a nastepnie wartosci te przekazywane sa do fukcji bibliotecznej rysujacej elipse. */ void drawCircle(int x, int y, int r) { int x1 = x-r, x2 = x+r, y1 = y+r, y2 = y-r; std::cout << "\tLibraryB ELIPSE//" << "p1=(" << x1 << ", " << y1 << "), " << "p2=(" << x2 << ", " << y2 << ")" << std::endl; } }; /** Abstrakcja. Abstrakcyjna klasa bazowa dla wszystkich ksztaltow ktore mozna narysowac za pomoca linii i kol. * Klasa dostarcza interfejsu klasom pochodnym (metoda draw()) oraz chornionych metod rysujacych linie i kolo * ktore wywoluja odpowiednio metody drawLinie i drawCircle interfejsu bibliotek. Klasa przechowuje * wskazanie na obiekt biblioteki uzywanej do rysowania. */ class AbstractShape { public: AbstractShape(ImplementationIterface* impl) : impl_(impl) {} ~AbstractShape() {} /** Czysto wirtualna metoda musi byc zaimplementowana w klasach pochodnych. * Metoda w klasach pochodnych rysuje konkretny ksztalt. */ virtual void draw() = 0; protected: /** Ponizsze metody stanowia opakowanie metod dostarczonych przez interfejs bibliotek. * Wewnatrz metod sa wywolywane odpowiednie metody interfejsu. Metody sluza komunikacji * pomiedzy klasami pochodnymi klasy AbstractShape a interfejsem bibliotek. Ukrywaja * szczegoly implementacji przed uzytkownikiem. W klasach pochodnych po AbstractShape mozna * wywolywac te metody aby narysowac ksztalt. */ void drawLine(int x1, int y1, int x2, int y2) { impl_->drawLine(x1, y1, x2, y2); } void drawCircle(int x, int y, int r) { impl_->drawCircle(x, y, r); } private: /** Wskazanie na interfejs bibliotek. W czasie tworzenia obiektu klasy pochodnej konstruktor * klasy bazowej inicjuje pole wskazaniem na obiekt klasy reprezentujacej konkretna biblioteke * za pomoca ktorej beda rysowane ksztalty tworzonego obiektu. */ ImplementationIterface *impl_; }; /** Klasa pochodna po klasie AbstractShape, reprezentuje trojkat.*/ class Triangle : public AbstractShape { public: /**Inicjuje wartosci pol klasy (wspolrzedne wierzcholkow) oraz wskazanie na klase bazowa implementacji.*/ Triangle(int x1, int y1, int x2, int y2, int x3, int y3, ImplementationIterface* impl) : AbstractShape(impl), x1_(x1), y1_(y1), x2_(x2), y2_(y2), x3_(x3), y3_(y3) {} ~Triangle() {} /** Rysuje trojkat zbudowany z trzech linii. Kazda linia jest rysowana przez wywolanie * metody chronionej z klasy bazowej, ktora rysuje linie z wykorzystaniem biblioteki na ktorej obiekt * wskazuje pole impl_ w klasie bazowej. */ void draw() { std::cout << "Rysuje trojkat " << std::endl; drawLine(x1_, y1_, x2_, y2_); drawLine(x2_, y2_, x3_, y3_); drawLine(x3_, y3_, x1_, y1_); } private: /** wspolrzedne wierzcholkow trojkata */ int x1_, y1_, x2_, y2_, x3_, y3_; }; /** Klasa pochodna po klasie AbstractShape, reprezentuje ksztal zlozony z prostokatu i kola. */ class CircleInRect : public AbstractShape { public: /**Inicjuje wartosci pol klasy (wspolrzedne wierzcholkow, srodek i promien kola) * oraz wskazanie na klase bazowa implementacji. */ CircleInRect(int x1, int y1, int x2, int y2, ImplementationIterface* impl) : AbstractShape(impl), x1_(x1), y1_(y1), x2_(x2), y2_(y2) { x_ = x2_ - x1_; y_ = y2_ - y1_; abs(x_) < abs(y_) ? r_ = abs(x_) : r_ = abs(y_); } ~CircleInRect() {} /** Rysuje prostokat zbudowany z czterech linii i kolo. Kazda linia(kolo) jest rysowana przez * wywolanie metody chronionej z klasy bazowej, ktora rysuje linie(kolo) z wykorzystaniem * biblioteki na ktorej obiekt wskazuje pole impl_ w klasie bazowej. */ void draw() { std::cout << "Rysuje kolo w prostokacie" << std::endl; drawLine(x1_, y1_, x2_, y1_); drawLine(x2_, y1_, x2_, y2_); drawLine(x2_, y2_, x1_, y2_); drawLine(x1_, y2_, x1_, y1_); drawCircle(x_, y_, r_); } private: /** wspolrzedne wierzcholkow prostokata oraz srodek i promien kola */ int x1_, y1_, x2_, y2_, x_, y_, r_; }; /** * TEST - tworzy obiekty klas pochodnych po klasie AbstraktShape z wykrozystaniem obu konkretnych * klas implementacji. Nastepnie wywoluje funkcje rysujaca ksztalt dla wszystkich obiektow. */ int main() { LibraryA lA; //tworzy obiekt reprezentujacy biblioteke A LibraryB lB; //tworzy obiekt reprezentujacy biblioteke B Triangle triA(0, 0, 2, 3, 4, 9, &lA); //tworzy obiekt klasy Triangle wykorzystujacy biblioteke A Triangle triB(-1, 2, 0, 5, 1, -1, &lB); //tworzy obiekt klasy Triangle wykorzystujacy biblioteke B triA.draw(); //rysuje trojkat z wykorzystaniem biblioteki A triB.draw(); //rysuje trojkat z wykorzystaniem biblioteki B CircleInRect cirA(0, 0, 10, 10, &lA); //tworzy obiekt klasy Circle wykorzystujacy biblioteke A CircleInRect cirB(-5, 0, 5, -10, &lB); //tworzy obiekt klasy Circle wykorzystujacy biblioteke B cirA.draw(); //rysuje prostokat i kolo z wykorzystaniem biblioteki A cirB.draw(); //rysuje prostokat i kolo z wykorzystaniem biblioteki B return 0; } /** Wynik testu: Rysuje trojkat LibraryA LINE1//p1=(0, 0), p2(2, 3) LibraryA LINE1//p1=(2, 3), p2(4, 9) LibraryA LINE1//p1=(4, 9), p2(0, 0) Rysuje trojkat LibraryB LINE2//pp =(-1, 2) lenght = 3.16228 angle = 1.24905 LibraryB LINE2//pp =(0, 5) lenght = 6.08276 angle = 1.40565 LibraryB LINE2//pp =(1, -1) lenght = 3.60555 angle = 0.982794 Rysuje kolo w prostokacie LibraryA LINE1//p1=(0, 0), p2(10, 0) LibraryA LINE1//p1=(10, 0), p2(10, 10) LibraryA LINE1//p1=(10, 10), p2(0, 10) LibraryA LINE1//p1=(0, 10), p2(0, 0) LibraryA CIRCLE//O =(10, 10) r =10 Rysuje kolo w prostokacie LibraryB LINE2//pp =(-5, 0) lenght = 10 angle = 0 LibraryB LINE2//pp =(5, 0) lenght = 10 angle = 1.5708 LibraryB LINE2//pp =(5, -10) lenght = 10 angle = 0 LibraryB LINE2//pp =(-5, -10) lenght = 10 angle = 1.5708 LibraryB ELIPSE//p1=(0, 0), p2=(20, -20) */