女人自慰AV免费观看内涵网,日韩国产剧情在线观看网址,神马电影网特片网,最新一级电影欧美,在线观看亚洲欧美日韩,黄色视频在线播放免费观看,ABO涨奶期羡澄,第一导航fulione,美女主播操b

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

實踐GoF的23種設計模式實現:橋接模式

元閏子的邀請 ? 來源:元閏子的邀請 ? 2024-04-14 09:30 ? 次閱讀

簡介

GoF 對橋接模式(Bridge Pattern)的定義如下:

Decouple an abstraction from its implementation so that the two can vary independently.

也即,將抽象部分和實現部分進行解耦,使得它們能夠各自往獨立的方向變化

橋接模式解決了在模塊有多種變化方向的情況下,用繼承所導致的類爆炸問題。

舉個例子,一個產品有形狀和顏色兩個特征(變化方向),其中形狀分為方形和圓形,顏色分為紅色和藍色。如果采用繼承的設計方案,那么就需要新增4個產品子類:方形紅色、圓形紅色、方形藍色、圓形紅色。如果形狀總共有 m 種變化,顏色有 n 種變化,那么就需要新增 m * n 個產品子類!

現在我們使用橋接模式進行優化,將形狀和顏色分別設計為抽象接口獨立出來,這樣需要新增 2 個形狀子類:方形和圓形,以及 2 個顏色子類:紅色和藍色。同樣,如果形狀總共有 m 種變化,顏色有 n 種變化,總共只需要新增 m + n 個子類!

1f8ae0a2-f948-11ee-a297-92fbcf53809c.png

上述例子中,我們通過將形狀和顏色抽象為一個接口,使產品不再依賴于具體的形狀和顏色細節,從而達到了解耦的目的。橋接模式本質上就是面向接口編程,可以給系統帶來很好的靈活性和可擴展性。如果一個對象存在多個變化的方向,而且每個變化方向都需要擴展,那么使用橋接模式進行設計那是再合適不過了。

當然,Go 語言從語言特性本身就把繼承剔除,但橋接模式中分離變化、面向接口編程的思想仍然值得學習。

UML 結構

1fb4db3c-f948-11ee-a297-92fbcf53809c.png

場景上下文

在簡單的分布式應用系統(示例代碼工程)中,我們設計了一個 Monitor 監控系統模塊,它可以看成是一個簡單的 ETL 系統,負責對監控數據進行采集、處理、輸出。監控數據來源于在線商場服務集群各個服務,當前通過消息隊列模塊 Mq 傳遞到監控系統,經處理后,存儲到數據庫模塊 Db 上。

1fe1c4f8-f948-11ee-a297-92fbcf53809c.jpg

我們假設未來要上線一個不支持對接消息隊列的服務、結果數據也需要存儲到 ClickHouse 以供后續分析,為了應對未來多變的需求,我們有必要將監控系統設計得足夠的可擴展。

于是,整個模塊被設計為插件化風格的架構,Pipeline是數據處理的流水線,其中包含了Input、Filter和Output三類插件,Input負責從各類數據源中獲取監控數據,Filter負責數據處理,Output負責將處理后的數據輸出。

1fee1bc2-f948-11ee-a297-92fbcf53809c.png

上述設計中,我們抽象出Input、Filter和Output三類插件,它們各種往獨立的方向變化,最后在Pipeline上進行靈活組合,這使用橋接模式正合適。

20109e22-f948-11ee-a297-92fbcf53809c.png

代碼實現

//關鍵點1:明確產品的變化點,這里是input、filter和output三類插件,它們各自變化

//demo/monitor/input/input_plugin.go
packageinput

//關鍵點2:將產品的變化點抽象成接口,這里是input.Plugin,filter.Plugin和output.Plugin
//Plugin輸入插件
typePlugininterface{
plugin.Plugin
Input()(*plugin.Event,error)
}

//關鍵點3:實現產品變化點的接口,這里是SocketInput,AddTimestampFilter和MemoryDbOutput
//demo/monitor/input/socket_input.go
typeSocketInputstruct{
socketnetwork.Socket
endpointnetwork.Endpoint
packetschan*network.Packet
isUninstalluint32
}

func(s*SocketInput)Input()(*plugin.Event,error){
packet,ok:=<-s.packets
????if?!ok?{
????????return?nil,?plugin.ErrPluginUninstalled
????}
????event?:=?plugin.NewEvent(packet.Payload())
????event.AddHeader("peer",?packet.Src().String())
????return?event,?nil
}

//?demo/monitor/filter/filter_plugin.go
package?filter

//?Plugin?過濾插件
type?Plugin?interface?{
????plugin.Plugin
????Filter(event?*plugin.Event)?*plugin.Event
}

//?demo/monitor/filter/add_timestamp_filter.go
//?AddTimestampFilter?為MonitorRecord增加時間戳
type?AddTimestampFilter?struct?{
}

func?(a?*AddTimestampFilter)?Filter(event?*plugin.Event)?*plugin.Event?{
????re,?ok?:=?event.Payload().(*model.MonitorRecord)
????if?!ok?{
????????return?event
????}
????re.Timestamp?=?time.Now().Unix()
????return?plugin.NewEvent(re)
}


//?demo/monitor/output/output_plugin.go
//?Plugin?輸出插件
type?Plugin?interface?{
????plugin.Plugin
????Output(event?*plugin.Event)?error
}

//?demo/monitor/output/memory_db_output.go
type?MemoryDbOutput?struct?{
????db????????db.Db
????tableName?string
}

func?(m?*MemoryDbOutput)?Output(event?*plugin.Event)?error?{
????r,?ok?:=?event.Payload().(*model.MonitorRecord)
????if?!ok?{
????return?fmt.Errorf("memory?db?output?unknown?event?type?%T",?event.Payload())
????}
????return?m.db.Insert(m.tableName,?r.Id,?r)
}

//?關鍵點4:定義產品的接口或者實現,通過組合的方式把變化點橋接起來。
//?demo/monitor/pipeline/pipeline_plugin.go
//?Plugin?pipeline由input、filter、output三種插件組成,定義了一個數據處理流程
//?數據流向為?input?->filter->output
//如果是接口,可以通過定義Setter方法達到聚合的目的。
typePlugininterface{
plugin.Plugin
SetInput(inputinput.Plugin)
SetFilter(filterfilter.Plugin)
SetOutput(outputoutput.Plugin)
}

//如果是結構體,直接把變化點作為成員變量來達到聚合的目的。
typepipelineTemplatestruct{
inputinput.Plugin
filterfilter.Plugin
outputoutput.Plugin
isCloseuint32
runfunc()
}

func(p*pipelineTemplate)SetInput(inputinput.Plugin){
p.input=input
}

func(p*pipelineTemplate)SetFilter(filterfilter.Plugin){
p.filter=filter
}

func(p*pipelineTemplate)SetOutput(outputoutput.Plugin){
p.output=output
}

//demo/monitor/pipeline/simple_pipeline.go
//SimplePipeline簡單Pipeline實現,每次運行時新啟一個goroutine
typeSimplePipelinestruct{
pipelineTemplate
}

在本系統中,我們通過配置文件來靈活組合插件,利用反射來實現插件的實例化,實例化的實現使用了抽象工廠模式,詳細的實現方法可參考【Go實現】實踐GoF的23種設計模式:抽象工廠模式。

總結實現橋接模式的幾個關鍵點:

明確產品的變化點,這里是 input、filter 和 output 三類插件,它們各自變化。

將產品的變化點抽象成接口,這里是input.Plugin,filter.Plugin和output.Plugin。

實現產品變化點的接口,這里是SocketInput,AddTimestampFilter和MemoryDbOutput。

定義產品的接口或者實現,通過組合的方式把變化點橋接起來。這里是pipeline.Plugin通過Setter方法將input.Plugin,filter.Plugin和output.Plugin三個抽象接口橋接了起來。后面即可實現各類 input、filter 和 output 的靈活組合了。

擴展

TiDB 中的橋接模式

TiDB是一款出色的分布式關系型數據庫,它對外提供了一套插件框架,方便用戶進行功能擴展。TiDB 的插件框架的設計,也運用到了橋接模式的思想。

203ccb32-f948-11ee-a297-92fbcf53809c.png

如上圖所示,每個Plugin都包含Validate、OnInit、OnShutdown、OnFlush四個待用戶實現的接口,它們可以按照各自的方向去變化,然后靈活組合在Plugin中。

//PluginpresentsaTiDBplugin.
typePluginstruct{
*Manifest
library*gplugin.Plugin
Pathstring
Disableduint32
StateState
}

//Manifestdescribesplugininfoandhowitcandobypluginitself.
typeManifeststruct{
Namestring
Descriptionstring
RequireVersionmap[string]uint16
Licensestring
BuildTimestring
//Validatedefinesthevalidatelogicforplugin.
//returnserrorwillstoploadpluginprocessandTiDBstartup.
Validatefunc(ctxcontext.Context,manifest*Manifest)error
//OnInitdefinestheplugininitlogic.
//itwillbecalledafterdomaininit.
//returnerrorwillstoploadpluginprocessandTiDBstartup.
OnInitfunc(ctxcontext.Context,manifest*Manifest)error
//OnShutDowndefinestheplugincleanuplogic.
//returnerrorwillwritelogandcontinueshutdown.
OnShutdownfunc(ctxcontext.Context,manifest*Manifest)error
//OnFlushdefinesflushlogicafterexecuted`flushtidbplugins`.
//itwillbecalledafterOnInit.
//returnerrorwillwritelogandcontinuewatchfollowingflush.
OnFlushfunc(ctxcontext.Context,manifest*Manifest)error
flushWatcher*flushWatcher

Versionuint16
KindKind
}

TiDB 在實現插件框架時,使用函數式編程的方式來定義 OnXXX 接口,更具有 Go 風格。

典型應用場景

從多個維度上對系統/類/結構體進行擴展,如插件化架構。

在運行時切換不同的實現,如插件化架構。

用于構建與平臺無關的程序適配層。

優缺點

優點

可實現抽象不分與實現解耦,變化實現時,客戶端無須修改代碼,符合開閉原則。

每個分離的變化點都可以專注于自身的演進,符合單一職責原則。

缺點

過度的抽象(過度設計)會使得接口膨脹,導致系統復雜性變大,難以維護。

與其他模式的關聯

橋接模式通常與抽象工廠模式搭配使用,比如,在本文例子中,可以通過抽象工廠模式對各個 Plugin 完成實例化,詳情見【Go實現】實踐GoF的23種設計模式:抽象工廠模式。

文章配圖

可以在用Keynote畫出手繪風格的配圖中找到文章的繪圖方法。



審核編輯:劉清

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • UML
    UML
    +關注

    關注

    0

    文章

    122

    瀏覽量

    31092
  • 數據處理
    +關注

    關注

    0

    文章

    626

    瀏覽量

    29010
  • go語言
    +關注

    關注

    1

    文章

    158

    瀏覽量

    9272

原文標題:【Go實現】實踐GoF的23種設計模式:橋接模式

文章出處:【微信號:yuanrunzi,微信公眾號:元閏子的邀請】歡迎添加關注!文章轉載請注明出處。

收藏 人收藏

    評論

    相關推薦
    熱點推薦

    關于模式遇到的問題

    VMware使用模式,已經手動設置ip地址和主機ip同一網段,但是ifconfig顯示的ip地址卻和vmnet8(NAT)同一網段?怎么解決?
    發表于 10-16 15:06

    CentOS靜態IP配置(模式

    [2018-08-26]-[CentOS]CentOS靜態IP配置(模式
    發表于 05-12 08:27

    23基本的設計模式總結

    一樣。?提到設計模式,不得不感謝GoF(***,四人組),他們1995年出版的《設計模式》一書,第一次將設計模式提升到理論高度,并將之規范化。書中一共總結了
    發表于 03-01 06:08

    C#23設計模式【完整】

    C#23設計模式
    發表于 08-21 17:38 ?71次下載

    23java設計模式

    JAVA的設計模式經前人總結可以分為23 設計模式根據使用類型可以分為三: 1、創建模式
    發表于 09-23 15:17 ?1次下載

    實踐GoF23設計模式:命令模式簡介

    因此,我們需要對請求進行抽象,將上下文信息封裝到請求對象里,這其實就是命令模式,而該請求對象就是 Command。
    的頭像 發表于 01-13 16:36 ?1268次閱讀

    模式的目標與設計

    模式的目標是使對象的抽象部分與實現部分分離,使之可以分別獨立變化,以盡量避免產生耦合。
    的頭像 發表于 06-01 14:29 ?702次閱讀
    <b class='flag-5'>橋</b><b class='flag-5'>接</b><b class='flag-5'>模式</b>的目標與設計

    設計模式結構性:模式

    模式不是將兩個不相干的類鏈接,而是將一個需要多維度變化的類拆分成抽象部分和實現部分,并且在抽象層對兩者做組合關聯,是用組合的方式來解決繼承的問題。
    的頭像 發表于 06-08 10:49 ?1021次閱讀
    設計<b class='flag-5'>模式</b>結構性:<b class='flag-5'>橋</b><b class='flag-5'>接</b><b class='flag-5'>模式</b>

    遠程網關模式實現同一局域網組網管理(Superlink)

    助虛擬網卡技術,先由服務器平臺下發網絡參數,然后將各ZP網關及下掛用戶終端直接建立在同一個局域網通道之內,實現各網絡節點設備可以自由
    的頭像 發表于 04-26 15:32 ?1524次閱讀
    遠程網關<b class='flag-5'>橋</b><b class='flag-5'>接</b><b class='flag-5'>模式</b><b class='flag-5'>實現</b>同一局域網組網管理(Superlink)

    模式應用場景

    模式(Bridge Pattern):將抽象和實現解耦, 使得兩者可以獨立地變化。 另外一解釋是:一個類存在兩個(或多個)獨立變化的
    的頭像 發表于 10-09 14:30 ?1519次閱讀
    <b class='flag-5'>橋</b><b class='flag-5'>接</b><b class='flag-5'>模式</b>應用場景

    實踐GoF23設計模式:備忘錄模式

    相對于代理模式、工廠模式等設計模式,備忘錄模式(Memento)在我們日常開發中出鏡率并不高,除了應用場景的限制之外,另一個原因,可能是備忘錄模式
    的頭像 發表于 11-25 09:05 ?760次閱讀
    <b class='flag-5'>實踐</b><b class='flag-5'>GoF</b>的<b class='flag-5'>23</b><b class='flag-5'>種</b>設計<b class='flag-5'>模式</b>:備忘錄<b class='flag-5'>模式</b>

    實踐GoF23設計模式:適配器模式

    適配器模式所做的就是將一個接口 Adaptee,通過適配器 Adapter 轉換成 Client 所期望的另一個接口 Target 來使用,實現原理也很簡單,就是 Adapter 通過實現 Target接口,并在對應的方法中調用
    的頭像 發表于 12-10 14:00 ?715次閱讀
    <b class='flag-5'>實踐</b><b class='flag-5'>GoF</b>的<b class='flag-5'>23</b><b class='flag-5'>種</b>設計<b class='flag-5'>模式</b>:適配器<b class='flag-5'>模式</b>

    VMware虛擬機的三網絡模式

    。VMware提供了三網絡模式模式、NAT模式和主機
    的頭像 發表于 02-04 11:17 ?2514次閱讀

    實踐GoF23設計模式:解釋器模式

    解釋器模式(Interpreter Pattern)應該是 GoF23 設計模式中使用頻率最少的一
    的頭像 發表于 04-01 11:01 ?912次閱讀
    <b class='flag-5'>實踐</b><b class='flag-5'>GoF</b>的<b class='flag-5'>23</b><b class='flag-5'>種</b>設計<b class='flag-5'>模式</b>:解釋器<b class='flag-5'>模式</b>

    網絡模式是什么? 網絡模式和路由模式的區別

    網絡模式是一網絡連接方式,它可以將多個設備連接在一起,使它們可以相互通信。在網絡
    的頭像 發表于 05-10 13:48 ?5365次閱讀