Wzór mostka
Wzorzec pomostowy to wzorzec projektowy używany w inżynierii oprogramowania , który ma na celu „oddzielenie abstrakcji od jej implementacji , tak aby oba mogły się różnić niezależnie” , wprowadzony przez Gang of Four . Most wykorzystuje enkapsulację , agregację i może wykorzystywać dziedziczenie do rozdzielania obowiązków na różne klasy .
Kiedy klasa często się zmienia, funkcje programowania obiektowego stają się bardzo przydatne, ponieważ zmiany w kodzie programu można łatwo wprowadzić przy minimalnej wcześniejszej wiedzy o programie. Wzorzec mostka jest przydatny, gdy zarówno klasa, jak i to, co robi, często się zmienia. Sama klasa może być traktowana jako abstrakcja , a to, co klasa może zrobić, to implementacja . Wzór mostu można również traktować jako dwie warstwy abstrakcji.
Gdy istnieje tylko jedna stała implementacja, ten wzorzec jest znany jako idiom Pimpl w świecie C++ .
Wzorzec mostka jest często mylony ze wzorcem adaptera i często jest implementowany przy użyciu wzorca adaptera obiektowego ; np. w poniższym kodzie Java.
Wariant: Implementację można jeszcze bardziej oddzielić, odkładając obecność implementacji do punktu, w którym abstrakcja jest wykorzystywana.
Przegląd
Wzorzec projektowy Bridge jest jednym z dwudziestu trzech dobrze znanych wzorców projektowych GoF , które opisują, jak rozwiązywać powtarzające się problemy projektowe w celu zaprojektowania elastycznego i wielokrotnego użytku zorientowanego obiektowo oprogramowania, czyli obiektów, które są łatwiejsze do wdrożenia, zmiany, testowania i ponowne użycie.
Jakie problemy może rozwiązać wzorzec projektowy Bridge?
- Abstrakcja i jej implementacja powinny być definiowane i rozszerzane niezależnie od siebie.
- Należy unikać powiązania w czasie kompilacji między abstrakcją a jej implementacją, aby można było wybrać implementację w czasie wykonywania.
Podczas korzystania z podklas, różne podklasy implementują klasę abstrakcyjną na różne sposoby. Ale implementacja jest powiązana z abstrakcją w czasie kompilacji i nie można jej zmienić w czasie wykonywania.
Jakie rozwiązanie opisuje wzorzec projektowy Bridge?
- Oddziel abstrakcję (
Abstraction
) od jej implementacji (Implementor
), umieszczając je w osobnych hierarchiach klas. - Zaimplementuj
Abstrakcję
w kategoriach (delegując do) obiektuImplementor
.
Umożliwia to skonfigurowanie abstrakcji
z obiektem Implementor
w czasie wykonywania. Zobacz także diagram klas i sekwencji Unified Modeling Language poniżej.
Struktura
Diagram klas i sekwencji UML
Na powyższym diagramie klas Unified Modeling Language abstrakcja ( Abstraction
) nie jest zaimplementowana jak zwykle w pojedynczej hierarchii dziedziczenia. Zamiast tego istnieje jedna hierarchia dla abstrakcji ( Abstraction
) i oddzielna hierarchia dla jej implementacji ( Implementor
), co czyni je niezależnymi od siebie. Interfejs abstrakcji
( operation()
) jest implementowany w kategoriach (poprzez delegowanie do) interfejsu Implementora
( imp.operationImp()
). Diagram sekwencji UML pokazuje interakcje w czasie wykonywania: Obiekt Abstract1
deleguje implementację do obiektu Implementor1
(poprzez wywołanie operationImp()
na Implementor1
), który wykonuje operację i powraca do Abstraction1
.
Diagram klas
- Abstrakcja (klasa abstrakcyjna)
- definiuje abstrakcyjny interfejs,
- który utrzymuje referencję Implementora.
- RefinedAbstraction (klasa normalna)
- rozszerza interfejs zdefiniowany przez Abstraction
- Implementor (interfejs)
- definiuje interfejs dla klas implementacji
- ConcreteImplementor (klasa normalna)
- implementuje interfejs Implementor
Przykład
C#
Wzór mostu komponuje obiekty w strukturze drzewa. Oddziela abstrakcję od implementacji. Tutaj abstrakcja reprezentuje klienta, z którego zostaną wywołane obiekty. Poniżej podano przykład zaimplementowany w języku C#
// Pomaga w zapewnieniu prawdziwie oddzielonej architektury publicznego interfejsu IBridge { void Function1 (); pusta Funkcja2 (); } public class Bridge1 : IBridge { public void Function1 () { Console . WriteLine ( "Most1.Funkcja1" ); } public void Funkcja2 () { Konsola . WriteLine ( "Most1.Funkcja2" ); } } public class Bridge2 : IBridge { public void Function1 () { Console . WriteLine ( "Most2.Funkcja1" ); } public void Funkcja2 () { Konsola . WriteLine ( "Most2.Funkcja2" ); } } interfejs publiczny IAbstractBridge { void CallMethod1 (); nieważna metoda wywołania2 (); } klasa publiczna AbstractBridge : IAbstractBridge { publiczny most IBridge ; public AbstractBridge ( most IBridge ) { to . most = most ; } public void CallMethod1 () { this . most . Funkcja1 (); } public void CallMethod2 () { this . most . Funkcja2 (); } }
Klasy Bridge to Implementacja, która używa tej samej architektury zorientowanej na interfejs do tworzenia obiektów. Z drugiej strony abstrakcja pobiera instancję klasy implementacji i uruchamia jej metodę. W ten sposób są one całkowicie oddzielone od siebie.
Kryształ
Klasa abstrakcyjna DrawingAPI abstract def draw_circle ( x : Float64 , y : Float64 , radius : Float64 ) end class DrawingAPI1 < DrawingAPI def draw_circle ( x : Float , y : Float , radius : Float ) "API1.circle at #{ x } : # { y } - radius: #{ radius } " end end class DrawingAPI2 < DrawingAPI def draw_circle ( x : Float64 , y : Float64 , radius : Float64 ) "API2.circle at #{ x } : #{ y } - promień: # { radius } " end end abstract class Shape protected getter drawing_api : DrawingAPI def initialize ( @drawing_api ) end abstract def draw abstract def resize_by_percentage ( percent : Float64 ) end class CircleShape < Shape getter x : Float64 getter y : Float64 getter radius : Float64 def inicjalizacja ( @x , @y , @radius , drawing_api : DrawingAPI ) super ( drawing_api ) koniec def draw @drawing_api . draw_circle ( @x , @y , @radius ) end def resize_by_percentage ( procent : Float64 ) @radius *= ( 1 + percent / 100 ) end end class BridgePattern def self . kształty testowe = [ ] kształtów kształtów << CircleShape . nowe ( 1.0 , 2.0 , 3.0 , DrawingAPI1 . nowe ) kształty << CircleShape . nowe ( 5.0 , 7.0 , 11.0 , DrawingAPI2 . nowe ) kształty . każdy robi | kształt | kształt . resize_by_percentage ( 2.5 ) nadaje kształt . rysuj koniec koniec koniec BridgePattern . test
Wyjście
API1.circle na 1.0:2.0 - promień: 3.075 API2.circle na 5.0:7.0 - promień: 11.275
C++
0
0
0
0
#include <iostream> #include <string> #include <wektor> class DrawingAPI { public : virtual ~ DrawingAPI () = default ; virtual std :: string DrawCircle ( float x , float y , float promień ) const = ; }; class DrawingAPI01 : public DrawingAPI { public : std :: string DrawCircle ( float x , float y , float radius ) const override { return "API01.circle at" + std :: to_string ( x ) + ":" + std :: to_string ( y ) + " - promień: " + std :: to_string ( promień ); } }; class DrawingAPI02 : public DrawingAPI { public : std :: string DrawCircle ( float x , float y , float radius ) const override { return "API02.circle at" + std :: to_string ( x ) + ":" + std :: to_string ( y ) + " - promień: " + std :: to_string ( promień ); } }; class Shape { public : Shape ( const DrawingAPI & drawing_api ) : drawing_api_ ( drawing_api ) {} virtual ~ Shape () = default ; virtual std :: string Draw () const = ; virtual float ResizeByPercentage ( const float percent ) = ; chroniony : const DrawingAPI & drawing_api_ ; }; class CircleShape : public Shape { public : CircleShape ( float x , float y , float promień , const DrawingAPI & drawing_api ) : Shape ( drawing_api ), x_ ( x ), y_ ( y ), radius_ ( radius ) {} std :: string Draw () const override { return drawing_api_ . RysujOkrąg ( x_ , y_ , promień_ ); } float ResizeByPercentage ( const float percent ) override { return radius_ *= ( 1.0f + procent / 100.0f ); } prywatne : float x_ , y_ , promień_ ; }; int main ( int argc , char ** argv ) { const DrawingAPI01 api1 {}; const DrawingAPI02 api2 {}; std :: vector < kształt okręgu > kształty { kształt okręgu { 1.0f , 2.0f , 3.0f , api1 }, kształt okręgu { 5.0f , 7.0f , 11.0f , api2 } }; for ( auto & kształt : kształty ) { kształt . ResizeByPercentage ( 2.5 ); std :: cout << kształt . Rysuj () << std :: endl ; } powrót ; }
Wyjście:
API01.circle na 1.000000:2.000000 - promień: 3.075000 API02.circle na 5.000000:7.000000 - promień: 11.275000
Jawa
Poniższy program Java definiuje konto bankowe, które oddziela operacje na koncie od rejestrowania tych operacji.
// Logger ma dwie implementacje: info i ostrzeżenie @FunctionalInterface interface Logger { void log ( String message ); static Informacje o rejestratorze () { wiadomość zwrotna -> System . na zewnątrz println ( "informacje:" + wiadomość ); } static Ostrzeżenie rejestratora () { komunikat zwrotny -> System . na zewnątrz println ( "ostrzeżenie: " + wiadomość ); } } Klasa abstrakcyjna AbstractAccount { private Logger logger = Logger . informacje (); public void setLogger ( Logger rejestratora ) { to . rejestrator = rejestrator ; } // część rejestrowania jest przekazywana do implementacji Loggera chronionej przed operacją void ( Komunikat łańcuchowy , wynik boolowski ) { logger . dziennik ( wiadomość + „wynik” + wynik ); } } class SimpleAccount extends AbstractAccount { private int balance ; public SimpleAccount ( int balance ) { to . równowaga = równowaga ; } public boolean isBalanceLow () { saldo zwrotu < 50 ; } public void wycofaj ( int kwota ) { boolean shouldPerform = saldo >= kwota ; if ( należy wykonać ) { saldo -= kwota ; } działać ( "wypłata" + kwota , shouldPerform ); } } public class BridgeDemo { public static void main ( String [] args ) { SimpleAccount account = new SimpleAccount ( 100 ); konto . wycofać ( 75 ); if ( account . isBalanceLow ()) { // możesz także zmienić implementację Loggera w runtime account . setLogger ( Rejestrator . ostrzeżenie ()); } konto . wycofać ( 10 ); konto . wycofać ( 100 ); } }
Wyprowadzi:
info: wycofaj 75 wynik prawda ostrzeżenie: wycofaj 10 wynik prawda ostrzeżenie: wycofaj 100 wynik fałsz
PHP
interfejs DrawingAPI { funkcja drawCircle ( $x , $y , $radius ); } class DrawingAPI1 implementuje DrawingAPI { public function drawCircle ( $x , $y , $radius ) { echo "API1.circle at $x : $y promień $radius . \n " ; } } class DrawingAPI2 implementuje DrawingAPI { public function drawCircle ( $x , $y , $radius ) { echo "API2.circle at $x : $y promień $radius . \n " ; } } Klasa abstrakcyjna Kształt { chroniony $drawingAPI ; rysowanie publicznej funkcji abstrakcyjnej (); publiczna funkcja abstrakcyjna resizeByPercentage ( $pct ); funkcja chroniona __construct ( DigitalAPI $drawingAPI ) { $this -> drawingAPI = $drawingAPI ; } } klasa CircleShape rozszerza Shape { private $x ; prywatne $y ; prywatny promień $ ; funkcja publiczna __construct ( $x , $y , $radius , DrawingAPI $drawingAPI ) { rodzic :: __construct ( $drawingAPI ); $to -> x = $x ; $to -> y = $y ; $to -> promień = $promień ; } rysowanie funkcji publicznej () { $to -> rysowanie API -> rysowanieOkręgu ( $ to -> x , $ to -> y , $ to -> promień ); } funkcja publiczna resizeByPercentage ( $pct ) { $this -> promień *= $pct ; } } class Tester { public static function main () { $shapes = array ( new CircleShape ( 1 , 3 , 7 , new DrawingAPI1 ()), new CircleShape ( 5 , 7 , 11 , new DrawingAPI2 ()), ); foreach ( $kształty jako $ kształt ) { $ kształt -> resizeByPercentage ( 2.5 ); $kształt -> rysuj (); } } } Tester :: main ();
Wyjście:
API1.circle w promieniu 1:3 17,5 API2.circle w promieniu 5:7 27,5
Scala
Cecha DrawingAPI { def drawCircle ( x : Double , y : Double , promień : Double ) } class DrawingAPI1 extends DrawingAPI { def drawCircle ( x : Double , y : Double , radius : Double ) = println ( s"API #1 $ x $ y $ promień " ) } class DrawingAPI2 extends DrawingAPI { def drawCircle ( x : Double , y : Double , radius : Double ) = println ( s"API #2 $ x $ y $ promień " ) } abstract class Shape ( drawingAPI : DrawingAPI ) { def draw ( ) def resizePercentage ( pct : Double ) } class CircleShape ( x : Double , y : Double , var radius : Double , drawingAPI : DrawingAPI ) extends Shape ( drawingAPI : DrawingAPI ) { def draw ( ) = drawingAPI . drawCircle ( x , y , promień ) def resizePercentage ( pct : Double ) { promień *= pct } } object BridgePattern { def main ( args : Array [ String ]) { Seq ( new CircleShape ( 1 , 3 , 5 , new DrawingAPI1 ) , nowy CircleShape ( 4 , 5 , 6 , nowy DrawingAPI2 ) ) foreach { x => x . zmień rozmiarProcent ( 3 ) x . rysuj () } } }
Pyton
""" Przykład wzorca mostka. """ z abc import ABCMeta , abstractmethod NOT_IMPLEMENTED = "Powinieneś to zaimplementować." class DrawingAPI : __metaclass__ = ABCMeta @abstractmethod def draw_circle ( self , x , y , radius ): podnieś NotImplementedError ( NOT_IMPLEMENTED ) class DrawingAPI1 ( DrawingAPI ): def draw_circle ( self , x , y , radius ): return f "API1.circle at { x } : { y } - promień: { promień } " class DrawingAPI2 ( DrawingAPI ): def draw_circle ( self , x , y , radius ): return f "API2.circle at { x } : { y } - promień: { promień } " class DrawingAPI3 ( DrawingAPI ): def draw_circle ( self , x , y , radius ): return f "API3.circle at { x } : { y } - radius: { promień } " class Shape : __metaclass__ = ABCMeta drawing_api = Brak def __init__ ( self , drawing_api ): self . drawing_api = drawing_api @abstractmethod def draw ( self ): podnieś NotImplementedError ( NOT_IMPLEMENTED ) @abstractmethod def resize_by_percentage ( self , percent ): podnieś NotImplementedError ( NOT_IMPLEMENTED ) class CircleShape ( Shape ): def __init__ ( self , x , y , radius , drawing_api ) : ja . x = x ja . y = y siebie . promień = promień super ( kształt okręgu , własny ) . __init__ ( drawing_api ) def draw ( self ): zwraca self . rysunek_api . draw_circle ( self.x , self.y , self.radius ) def resize_by_percentage ( self , percent ) : self . _ _ _ _ _ promień *= 1 + procent / 100 klasa BridgePattern : @staticmethod def test (): Shapes = [ CircleShape ( 1.0 , 2.0 , 3.0 , DrawingAPI1 ()), CircleShape ( 5.0 , 7.0 , 11.0 , DrawingAPI2 ()), CircleShape ( 5.0 , 4.0 , 12.0 , DrawingAPI3 ()), ] dla kształtu w kształtach : kształt . resize_by_percentage ( 2.5 ) print ( kształt . rysuj ()) BridgePattern . test ()
Zobacz też
Linki zewnętrzne
- Bridge w UML iw LePUS3 (formalny język modelowania)
- Wzorce projektowe C#: wzorzec pomostowy . Przykładowy rozdział . 2002-12-20. Od: James W. Cooper (2003). Wzorce projektowe C#: samouczek . Addison-Wesley . ISBN 0-201-84453-2 .