1. 目的
將抽象部分與它的實作部分分離,使得它們都可以獨立地變化,
橋接模式(Bridge)也稱:柄體模式(Handle and Body)或介面模式(Interface),
2. 解讀
類比:
- 先舉一個生活中的例子,比如“蠟筆”,如果我們需要12種顏色,那么我們準備12支對應顏色的蠟筆即可,但如果我們需要大、中、小號的三種蠟筆,并且都要有12種顏色,我們可能不得不買3盒不同型號的蠟筆,并且每盒都包含12種顏色,
- 有沒有其它的思路呢?如果是毛筆呢?可不可以準備大、中、小三只毛筆,然后配上12種顏色盒呢? 兩種方案有什么不同?在這個場景中,如果使用蠟筆,我們將需要準備 3 * 12, 共計36支,但若使用毛筆,則只需要準備3支 + 12種對應的顏料盒即可,哈哈,找到不同點了么?😉
- 在這個例子中其實包含了兩個維度:型號與顏色,對于蠟筆,型號與顏色是“強耦合”關系,型號與顏色的變化是系結在一起的;但對于毛筆,型號與顏色是“弱耦合”關系,顏色維度(顏料盒)和型號(毛筆大小)維度可以獨立變化,
由上面的例子,可以引出我們面向物件世界中的一個重要模式,也即橋接模式(Bridge Pattern),這個模式的核心點在于分離不同維度的變化,例如之前提及到的預留出口類模式,就是橋接模式的一個典型應用場景,在本篇中,我們將進一步闡釋這個模式的內核思想,
應用場景:
- 在某些場景中,當物件有多個維度的變化(最典型的場景就是2個維度),例如抽象/平臺、前端/后端等,則可以使用橋接模式,橋接模式使得物件的其中任何一個維度都可以相互結合使用,
- 例如,不同作業系統下,對于不同圖片格式的處理和顯示,可以使用橋接模式,達成作業系統維度和圖片處理維度的分離,
- 再舉一個實際的例子,比如,在不同國家會計準則下,企業稅費的計算與報表出具,這個場景下的2個維度即“報表規范” 和“稅費的計算”,分別基于這連個維度建模,并使用橋接模式讓連個物件實作“聚合”,組成“松耦合”的架構,
- 此模式的核心在于,在建模程序中,充分理解和應用“聚合/合成”原則,
類圖:

各種角色:
- Abstraction - 抽象,定義了客戶端可以訪問的抽象介面(例如:定義的圖片顯示介面),并參考Implementor的實體物件,
- RefinedAbstraction - 被提煉的抽象,可進一步擴展和豐富Abstraction的行為(例如:實作abstraction介面,并豐富在不同作業系統下,對于顯卡的驅動動作)
- Implementor - 實作,為具體的實作定義了抽象的介面行為,(例如:圖片顯示)
- ConcreteImplementor - 具體的實作,實作具體的功能,(例如:jpg圖片的顯示,png圖片的顯示)
注意:通常情況下,抽象Abstraction部分的介面和實作Implementor部分的介面是不同的, 抽象Abstraction的介面是面向客戶端Client的,實作Implementor部分的介面是面向具體的實作物件的,
要點:
- “橋接模式” 的優點在于,其分離的抽象和實作之間的耦合關系,讓兩個維度可以獨立的變化,提升的架構的靈活性,
- 當然,“橋接模式” 的引入增加了系統設計的復雜性,需要更多的effort,同時,這種間接的參考關系,在一定程度上也對performance帶來一些影響,
體現的設計原則:
- 單一職責原則 - 按維度分工,職責單一而清晰
- 開放封閉原則 - 不同的維度間,可以獨立擴展
- 除此之外,橋接模式是合成/聚合復用原則的一個典型應用,“合成/聚合復用原則”告訴我們,要盡量使用合成/聚合的關系去完成設計,盡量不要使用類的繼承,因為繼承是一種“強依賴”關系,父類的變動會直接影響子類的變化,基于繼承關系建立模型,如若使用子類時,解決不了新的問題,則父類就會需要被重寫、或重新設計,這種“強依賴”關系限制了面向物件的靈活性,并最終限制了復用性,
- “合成/聚合復用原則”的好處在于,優先使用物件的合成/聚合將有助于保持每個類的封裝,并被集中到單個任務上,這樣的類和類的繼承層次會保持較小的規模,并且不太可能增長成為不可控制的龐然大物,
設計模式辨析:
- 配接器模式:配接器是從一些現有組件的介面中解耦一個抽象, 相反,橋模式通過設計提前實作解耦,并考慮到未來的可擴展性, 通常,配接器和Adaptee的介面有點相似,而橋模式則并非如此,
3. 舉例
3.1 橋接模式的典型代碼
下面給出了“橋接模式”的架構代碼,abstraction與Implementor進行了解耦合,通過合成關系(橋接)進行弱關聯,
REPORT zbridge_pattern.
**********************************************************************
* abstraction per dimensions
**********************************************************************
INTERFACE lif_implementor.
METHODS do_operation_1.
METHODS do_operation_2.
ENDINTERFACE.
CLASS lcl_abstraction DEFINITION ABSTRACT.
PUBLIC SECTION.
METHODS set_implementor
IMPORTING
io_imp TYPE REF TO lif_implementor.
METHODS do_action_1 ABSTRACT.
METHODS do_action_2 ABSTRACT.
PROTECTED SECTION.
DATA mo_implementor TYPE REF TO lif_implementor.
ENDCLASS.
CLASS lcl_abstraction IMPLEMENTATION.
METHOD set_implementor.
mo_implementor = io_imp.
ENDMETHOD.
ENDCLASS.
**********************************************************************
* implementation
**********************************************************************
CLASS lcl_implementor_a DEFINITION FINAL CREATE PUBLIC.
PUBLIC SECTION.
INTERFACES lif_implementor.
ENDCLASS.
CLASS lcl_implementor_a IMPLEMENTATION.
METHOD lif_implementor~do_operation_1.
WRITE / 'Do operation 1 in implementor A' .
ENDMETHOD.
METHOD lif_implementor~do_operation_2.
WRITE / 'Do operation 2 in implementor A' .
ENDMETHOD.
ENDCLASS.
CLASS lcl_implementor_b DEFINITION FINAL CREATE PUBLIC.
PUBLIC SECTION.
INTERFACES lif_implementor.
ENDCLASS.
CLASS lcl_implementor_b IMPLEMENTATION.
METHOD lif_implementor~do_operation_1.
WRITE / 'Do operation 1 in implementor B' .
ENDMETHOD.
METHOD lif_implementor~do_operation_2.
WRITE / 'Do operation 2 in implementor B' .
ENDMETHOD.
ENDCLASS.
**********************************************************************
CLASS lcl_refined_abstraction_x DEFINITION FINAL CREATE PUBLIC
INHERITING FROM lcl_abstraction.
PUBLIC SECTION.
METHODS:
do_action_1 REDEFINITION,
do_action_2 REDEFINITION.
ENDCLASS.
CLASS lcl_refined_abstraction_x IMPLEMENTATION.
METHOD do_action_1.
WRITE / 'Do action 1 in abstraction X'.
mo_implementor->do_operation_1( ).
ENDMETHOD.
METHOD do_action_2.
WRITE / 'Do action 2 in abstraction X'.
mo_implementor->do_operation_2( ).
ENDMETHOD.
ENDCLASS.
CLASS lcl_refined_abstraction_y DEFINITION FINAL CREATE PUBLIC
INHERITING FROM lcl_abstraction.
PUBLIC SECTION.
METHODS:
do_action_1 REDEFINITION,
do_action_2 REDEFINITION.
ENDCLASS.
CLASS lcl_refined_abstraction_y IMPLEMENTATION.
METHOD do_action_1.
WRITE / 'Do action 1 in abstraction Y'.
mo_implementor->do_operation_1( ).
ENDMETHOD.
METHOD do_action_2.
WRITE / 'Do action 2 in abstraction Y'.
mo_implementor->do_operation_2( ).
ENDMETHOD.
ENDCLASS.
**********************************************************************
START-OF-SELECTION.
**********************************************************************
DATA(lo_abstraction_x) = NEW lcl_refined_abstraction_x( ).
DATA(lo_abstraction_y) = NEW lcl_refined_abstraction_y( ).
DATA(lo_imp_a) = NEW lcl_implementor_a( ).
DATA(lo_imp_b) = NEW lcl_implementor_b( ).
lo_abstraction_x->set_implementor( lo_imp_a ).
lo_abstraction_x->do_action_1( ).
lo_abstraction_x->do_action_2( ).
lo_abstraction_y->set_implementor( lo_imp_b ).
lo_abstraction_y->do_action_1( ).
lo_abstraction_y->do_action_2( ).
運行結果:
可以看到,Abstraction X與 Implementor A進行組合,Abstraction Y與 Implementor B進行組合,當然,也可以直接實作,Abstraction X與 Implementor B進行組合,Abstraction Y與 Implementor A進行組合,
這就是橋接模式帶來的靈活性,

3.2 圖片顯示與作業系統的分離
下面給出了一個具體的例子,分離“圖片顯示”和“作業系統”兩個維度,從而使得兩個維度可以獨立變化,
REPORT zbridge_pattern2.
**********************************************************************
* abstraction per dimensions
**********************************************************************
INTERFACE lif_image_imp.
METHODS display.
ENDINTERFACE.
CLASS lcl_image DEFINITION ABSTRACT.
PUBLIC SECTION.
METHODS set_image_implementor
IMPORTING
io_imp TYPE REF TO lif_image_imp.
METHODS output_image ABSTRACT.
PROTECTED SECTION.
DATA mo_image_imp TYPE REF TO lif_image_imp.
ENDCLASS.
CLASS lcl_image IMPLEMENTATION.
METHOD set_image_implementor.
mo_image_imp = io_imp.
ENDMETHOD.
ENDCLASS.
**********************************************************************
* implementation
**********************************************************************
CLASS lcl_image_imp_jpg DEFINITION FINAL CREATE PUBLIC.
PUBLIC SECTION.
INTERFACES lif_image_imp.
ENDCLASS.
CLASS lcl_image_imp_jpg IMPLEMENTATION.
METHOD lif_image_imp~display.
WRITE / 'Display a .jpg file' .
ENDMETHOD.
ENDCLASS.
CLASS lcl_image_imp_png DEFINITION FINAL CREATE PUBLIC.
PUBLIC SECTION.
INTERFACES lif_image_imp.
ENDCLASS.
CLASS lcl_image_imp_png IMPLEMENTATION.
METHOD lif_image_imp~display.
WRITE / 'Display a .png file' .
ENDMETHOD.
ENDCLASS.
**********************************************************************
CLASS lcl_image_windows DEFINITION FINAL CREATE PUBLIC
INHERITING FROM lcl_image.
PUBLIC SECTION.
METHODS: output_image REDEFINITION.
ENDCLASS.
CLASS lcl_image_windows IMPLEMENTATION.
METHOD output_image.
WRITE / 'Prepare windows system environment...'.
mo_image_imp->display( ).
ENDMETHOD.
ENDCLASS.
CLASS lcl_image_linux DEFINITION FINAL CREATE PUBLIC
INHERITING FROM lcl_image.
PUBLIC SECTION.
METHODS: output_image REDEFINITION.
ENDCLASS.
CLASS lcl_image_linux IMPLEMENTATION.
METHOD output_image.
WRITE / 'Prepare Linux system environment...'.
mo_image_imp->display( ).
ENDMETHOD.
ENDCLASS.
**********************************************************************
START-OF-SELECTION.
**********************************************************************
DATA(lo_image_windows) = NEW lcl_image_windows( ).
DATA(lo_image_linux) = NEW lcl_image_linux( ).
DATA(lo_image_jpg) = NEW lcl_image_imp_jpg( ).
DATA(lo_image_png) = NEW lcl_image_imp_png( ).
lo_image_windows->set_image_implementor( lo_image_jpg ).
lo_image_windows->output_image( ).
lo_image_windows->set_image_implementor( lo_image_png ).
lo_image_windows->output_image( ).
lo_image_linux->set_image_implementor( lo_image_jpg ).
lo_image_linux->output_image( ).
lo_image_linux->set_image_implementor( lo_image_png ).
lo_image_linux->output_image( ).
運行結果:
可以看到,由于獨立了“作業系統”與“圖片顯示”兩個維度,使得兩種維度可以獨立變化和組合,

以上,是本篇對橋接模式的總結,歡迎分享、留言,😉
本博客專注于技術分享,干貨滿滿,持續更新,
歡迎關注??、點贊👍、轉發📣!
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/260390.html
標籤:其他
上一篇:springboot學習筆記1:什么是springboot
下一篇:為什么工廠模式可以解耦?(二)
