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.
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.
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 <iostream> #include <math.h> /** 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) */