日B视频 亚洲,啪啪啪网站一区二区,91色情精品久久,日日噜狠狠色综合久,超碰人妻少妇97在线,999青青视频,亚洲一区二卡,让本一区二区视频,日韩网站推荐

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

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

3天內(nèi)不再提示

GoF給裝飾者模式的定義

元閏子的邀請 ? 來源:元閏子的邀請 ? 作者:元閏子的邀請 ? 2022-06-29 10:22 ? 次閱讀
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

上一篇:【Go實現(xiàn)】實踐GoF的23種設計模式:原型模式

簡單的分布式應用系統(tǒng)(示例代碼工程):https://github.com/ruanrunxue/Practice-Design-Pattern--Go-Implementation

簡介

我們經(jīng)常會遇到“給現(xiàn)有對象/模塊新增功能”的場景,比如 http router 的開發(fā)場景下,除了最基礎的路由功能之外,我們常常還會加上如日志、鑒權(quán)、流控等 middleware。如果你查看框架的源碼,就會發(fā)現(xiàn) middleware 功能的實現(xiàn)用的就是裝飾者模式(Decorator Pattern)。

GoF給裝飾者模式的定義如下:

Decorators provide a flexible alternative to subclassing for extending functionality. Attach additional responsibilities to an object dynamically.

簡單來說,裝飾者模式通過組合的方式,提供了能夠動態(tài)地給對象/模塊擴展新功能的能力。理論上,只要沒有限制,它可以一直把功能疊加下去,具有很高的靈活性。

如果寫過 Java,那么一定對 I/O Stream 體系不陌生,它是裝飾者模式的經(jīng)典用法,客戶端程序可以動態(tài)地為原始的輸入輸出流添加功能,比如按字符串輸入輸出,加入緩沖等,使得整個 I/O Stream 體系具有很高的可擴展性和靈活性。

UML 結(jié)構(gòu)

0c30719e-f700-11ec-ba43-dac502259ad0.jpg

場景上下文

在簡單的分布式應用系統(tǒng)(示例代碼工程)中,我們設計了 Sidecar 邊車模塊,它的用處主要是為了 1)方便擴展network.Socket的功能,如增加日志、流控等非業(yè)務功能;2)讓這些附加功能對業(yè)務程序隱藏起來,也即業(yè)務程序只須關(guān)心看到network.Socket接口即可。

0c4d81d0-f700-11ec-ba43-dac502259ad0.jpg

代碼實現(xiàn)

Sidecar 的這個功能場景,很適合使用裝飾者模式來實現(xiàn),代碼如下:

//demo/network/socket.go
packagenetwork

//關(guān)鍵點1:定義被裝飾的抽象接口
//Socket網(wǎng)絡通信Socket接口
typeSocketinterface{
//Listen在endpoint指向地址上起監(jiān)聽
Listen(endpointEndpoint)error
//Close關(guān)閉監(jiān)聽
Close(endpointEndpoint)
//Send發(fā)送網(wǎng)絡報文
Send(packet*Packet)error
//Receive接收網(wǎng)絡報文
Receive(packet*Packet)
//AddListener增加網(wǎng)絡報文監(jiān)聽者
AddListener(listenerSocketListener)
}

//關(guān)鍵點2:提供一個默認的基礎實現(xiàn)
typesocketImplstruct{
listenerSocketListener
}

funcDefaultSocket()*socketImpl{
return&socketImpl{}
}

func(s*socketImpl)Listen(endpointEndpoint)error{
returnInstance().Listen(endpoint,s)
}
...//socketImpl的其他Socket實現(xiàn)方法


//demo/sidecar/flowctrl_sidecar.go
packagesidecar

//關(guān)鍵點3:定義裝飾器,實現(xiàn)被裝飾的接口
//FlowCtrlSidecarHTTP接收端流控功能裝飾器,自動攔截Socket接收報文,實現(xiàn)流控功能
typeFlowCtrlSidecarstruct{
//關(guān)鍵點4:裝飾器持有被裝飾的抽象接口作為成員屬性
socketnetwork.Socket
ctx*flowctrl.Context
}

//關(guān)鍵點5:對于需要擴展功能的方法,新增擴展功能
func(f*FlowCtrlSidecar)Receive(packet*network.Packet){
httpReq,ok:=packet.Payload().(*http.Request)
//如果不是HTTP請求,則不做流控處理
if!ok{
f.socket.Receive(packet)
return
}
//流控后返回429TooManyRequest響應
if!f.ctx.TryAccept(){
httpResp:=http.ResponseOfId(httpReq.ReqId()).
AddStatusCode(http.StatusTooManyRequest).
AddProblemDetails("enterflowctrlstate")
f.socket.Send(network.NewPacket(packet.Dest(),packet.Src(),httpResp))
return
}
f.socket.Receive(packet)
}

//關(guān)鍵點6:不需要擴展功能的方法,直接調(diào)用被裝飾接口的原生方法即可
func(f*FlowCtrlSidecar)Close(endpointnetwork.Endpoint){
f.socket.Close(endpoint)
}
...//FlowCtrlSidecar的其他方法

//關(guān)鍵點7:定義裝飾器的工廠方法,入?yún)楸谎b飾接口
funcNewFlowCtrlSidecar(socketnetwork.Socket)*FlowCtrlSidecar{
return&FlowCtrlSidecar{
socket:socket,
ctx:flowctrl.NewContext(),
}
}

//demo/sidecar/all_in_one_sidecar_factory.go
//關(guān)鍵點8:使用時,通過裝飾器的工廠方法,把所有裝飾器和被裝飾者串聯(lián)起來
func(aAllInOneFactory)Create()network.Socket{
returnNewAccessLogSidecar(NewFlowCtrlSidecar(network.DefaultSocket()),a.producer)
}

總結(jié)實現(xiàn)裝飾者模式的幾個關(guān)鍵點:

  1. 定義需要被裝飾的抽象接口,后續(xù)的裝飾器都是基于該接口進行擴展。
  2. 為抽象接口提供一個基礎實現(xiàn)。
  3. 定義裝飾器,并實現(xiàn)被裝飾的抽象接口。
  4. 裝飾器持有被裝飾的抽象接口作為成員屬性。“裝飾”的意思是在原有功能的基礎上擴展新功能,因此必須持有原有功能的抽象接口。
  5. 在裝飾器中,對于需要擴展功能的方法,新增擴展功能。
  6. 不需要擴展功能的方法,直接調(diào)用被裝飾接口的原生方法即可。
  7. 為裝飾器定義一個工廠方法,入?yún)楸谎b飾接口。
  8. 使用時,通過裝飾器的工廠方法,把所有裝飾器和被裝飾者串聯(lián)起來。

擴展

Go 風格的實現(xiàn)

在 Sidecar 的場景上下文中,被裝飾的Socket是一個相對復雜的接口,裝飾器通過實現(xiàn)Socket接口來進行功能擴展,是典型的面向?qū)ο箫L格。

如果被裝飾者是一個簡單的接口/方法/函數(shù),我們可以用更具 Go 風格的實現(xiàn)方式,考慮前文提到的 http router 場景。如果你使用原生的net/http進行 http router 開發(fā),通常會這么實現(xiàn):

funcmain(){
//注冊/hello的router
http.HandleFunc("/hello",hello)
//啟動http服務器
http.ListenAndServe("localhost:8080",nil)
}

//具體的請求處理邏輯,類型是http.HandlerFunc
funchello(whttp.ResponseWriter,r*http.Request){
w.Write([]byte("hello,world"))
}

其中,我們通過http.HandleFunc來注冊具體的 router,hello是具體的請求處理方法?,F(xiàn)在,我們想為該 http 服務器增加日志、鑒權(quán)等通用功能,那么可以把func(w http.ResponseWriter, r *http.Request)作為被裝飾的抽象接口,通過新增日志、鑒權(quán)等裝飾器完成功能擴展。

//demo/network/http/http_handle_func_decorator.go

//關(guān)鍵點1:確定被裝飾接口,這里為原生的http.HandlerFunc
typeHandlerFuncfunc(ResponseWriter,*Request)

//關(guān)鍵點2:定義裝飾器類型,是一個函數(shù)類型,入?yún)⒑头祷刂刀际莌ttp.HandlerFunc函數(shù)
typeHttpHandlerFuncDecoratorfunc(http.HandlerFunc)http.HandlerFunc

//關(guān)鍵點3:定義裝飾函數(shù),入?yún)楸谎b飾的接口和裝飾器可變列表
funcDecorate(hhttp.HandlerFunc,decorators...HttpHandlerFuncDecorator)http.HandlerFunc{
//關(guān)鍵點4:通過for循環(huán)遍歷裝飾器,完成對被裝飾接口的裝飾
for_,decorator:=rangedecorators{
h=decorator(h)
}
returnh
}

//關(guān)鍵點5:實現(xiàn)具體的裝飾器
funcWithBasicAuth(hhttp.HandlerFunc)http.HandlerFunc{
returnfunc(whttp.ResponseWriter,r*http.Request){
cookie,err:=r.Cookie("Auth")
iferr!=nil||cookie.Value!="Pass"{
w.WriteHeader(http.StatusForbidden)
return
}
//關(guān)鍵點6:完成功能擴展之后,調(diào)用被裝飾的方法,才能將所有裝飾器和被裝飾者串起來
h(w,r)
}
}

funcWithLogger(hhttp.HandlerFunc)http.HandlerFunc{
returnfunc(whttp.ResponseWriter,r*http.Request){
log.Println(r.Form)
log.Printf("path%s",r.URL.Path)
h(w,r)
}
}

funchello(whttp.ResponseWriter,r*http.Request){
w.Write([]byte("hello,world"))
}

funcmain(){
//關(guān)鍵點7:通過Decorate函數(shù)完成對hello的裝飾
http.HandleFunc("/hello",Decorate(hello,WithLogger,WithBasicAuth))
//啟動http服務器
http.ListenAndServe("localhost:8080",nil)
}

上述的裝飾者模式的實現(xiàn),用到了類似于Functional Options的技巧,也是巧妙利用了 Go 的函數(shù)式編程的特點,總結(jié)下來有如下幾個關(guān)鍵點:

  1. 確定被裝飾的接口,上述例子為http.HandlerFunc
  2. 定義裝飾器類型,是一個函數(shù)類型,入?yún)⒑头祷刂刀际潜谎b飾接口,上述例子為func(http.HandlerFunc) http.HandlerFunc。
  3. 定義裝飾函數(shù),入?yún)楸谎b飾的接口和裝飾器可變列表,上述例子為Decorate方法。
  4. 在裝飾方法中,通過for循環(huán)遍歷裝飾器,完成對被裝飾接口的裝飾。這里是用來類似Functional Options的技巧,一定要注意裝飾器的順序
  5. 實現(xiàn)具體的裝飾器,上述例子為WithBasicAuthWithLogger函數(shù)。
  6. 在裝飾器中,完成功能擴展之后,記得調(diào)用被裝飾者的接口,這樣才能將所有裝飾器和被裝飾者串起來。
  7. 在使用時,通過裝飾函數(shù)完成對被裝飾者的裝飾,上述例子為Decorate(hello, WithLogger, WithBasicAuth)。

Go 標準庫中的裝飾者模式

在 Go 標準庫中,也有一個運用了裝飾者模式的模塊,就是context,其中關(guān)鍵的接口如下:

packagecontext

//被裝飾接口
typeContextinterface{
Deadline()(deadlinetime.Time,okbool)
Done()<-chanstruct{}
Err()error
Value(keyany)any
}

//cancel裝飾器
typecancelCtxstruct{
Context//被裝飾接口
musync.Mutex
doneatomic.Value
childrenmap[canceler]struct{}=
errerror
}
//cancel裝飾器的工廠方法
funcWithCancel(parentContext)(ctxContext,cancelCancelFunc){
//...
c:=newCancelCtx(parent)
propagateCancel(parent,&c)
return&c,func(){c.cancel(true,Canceled)}
}

//timer裝飾器
typetimerCtxstruct{
cancelCtx//被裝飾接口
timer*time.Timer

deadlinetime.Time
}
//timer裝飾器的工廠方法
funcWithDeadline(parentContext,dtime.Time)(Context,CancelFunc){
//...
c:=&timerCtx{
cancelCtx:newCancelCtx(parent),
deadline:d,
}
//...
returnc,func(){c.cancel(true,Canceled)}
}
//timer裝飾器的工廠方法
funcWithTimeout(parentContext,timeouttime.Duration)(Context,CancelFunc){
returnWithDeadline(parent,time.Now().Add(timeout))
}

//value裝飾器
typevalueCtxstruct{
Context//被裝飾接口
key,valany
}
//value裝飾器的工廠方法
funcWithValue(parentContext,key,valany)Context{
ifparent==nil{
panic("cannotcreatecontextfromnilparent")
}
//...
return&valueCtx{parent,key,val}
}
0c708ad6-f700-11ec-ba43-dac502259ad0.jpg

使用時,可以這樣:

//使用時,可以這樣
funcmain(){
ctx:=context.Background()
ctx=context.WithValue(ctx,"key1","value1")
ctx,_=context.WithTimeout(ctx,time.Duration(1))
ctx=context.WithValue(ctx,"key2","value2")
}

不管是 UML 結(jié)構(gòu),還是使用方法,context模塊都與傳統(tǒng)的裝飾者模式有一定出入,但也不妨礙context是裝飾者模式的典型運用。還是那句話,學習設計模式,不能只記住它的結(jié)構(gòu),而是學習其中的動機和原理。

典型使用場景

  • I/O 流,比如為原始的 I/O 流增加緩沖、壓縮等功能。
  • Http Router,比如為基礎的 Http Router 能力增加日志、鑒權(quán)、Cookie等功能。
  • ......

優(yōu)缺點

優(yōu)點

  1. 遵循開閉原則,能夠在不修改老代碼的情況下擴展新功能。
  2. 可以用多個裝飾器把多個功能組合起來,理論上可以無限組合。

缺點

  1. 一定要注意裝飾器裝飾的順序,否則容易出現(xiàn)不在預期內(nèi)的行為。
  2. 當裝飾器越來越多之后,系統(tǒng)也會變得復雜。

與其他模式的關(guān)聯(lián)

裝飾者模式和代理模式具有很高的相似性,但是兩種所強調(diào)的點不一樣。前者強調(diào)的是為本體對象添加新的功能;后者強調(diào)的是對本體對象的訪問控制。

裝飾者模式和適配器模式的區(qū)別是,前者只會擴展功能而不會修改接口;后者則會修改接口。

文章配圖

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

審核編輯 :李倩


聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學習之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • 模塊
    +關(guān)注

    關(guān)注

    7

    文章

    2849

    瀏覽量

    53458
  • UML
    UML
    +關(guān)注

    關(guān)注

    0

    文章

    123

    瀏覽量

    31663

原文標題:【Go實現(xiàn)】實踐GoF的23種設計模式:裝飾者模式

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

收藏 人收藏
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

    評論

    相關(guān)推薦
    熱點推薦

    [VirtualLab] Ince-Gaussian模式

    摘要 除了厄米和拉蓋爾高斯光束模式外,波動方程在傍軸情況還有第三種嚴格的正交解系——即所謂的Ince-Gaussian光束。這些解在橢圓坐標系中定義,并且允許通過橢圓參數(shù)實現(xiàn)厄米和拉蓋爾高斯光束模式
    發(fā)表于 03-30 09:23

    VirtualLab:Ince高斯模式

    **摘要 ** 除了Hermite和Laguerre高斯模式之外,近軸波動方程還有第三種嚴格的正交解族,即所謂的Ince高斯模式。這些解在橢圓坐標中定義,并且通過橢圓參數(shù)允許在Hermite
    發(fā)表于 03-20 08:58

    VirtualLab:Ince高斯模式

    **摘要 ** 除了Hermite和Laguerre高斯模式之外,近軸波動方程還有第三種嚴格的正交解族,即所謂的Ince高斯模式。這些解在橢圓坐標中定義,并且通過橢圓參數(shù)允許在Hermite
    發(fā)表于 03-19 08:36

    KiCad 10 IPC API 開發(fā)問答整理

    : 兼容性保留: ?依賴舊版?pcbnew.py?(SWIG) 的插件在 KiCad 10 中依然可以使用,了開發(fā)更多的緩沖時間。 功能缺席: ? 無頭模式 (Headless Mode
    的頭像 發(fā)表于 01-07 11:20 ?975次閱讀

    嵌入式程序設計中4種常用模式

    。 如果不具備此條件,則必須作為框架的重要約定,禁止二次開發(fā)產(chǎn)生此類問題。 4. 裝飾模式 裝飾模式賦予了框架在后期增加功能的能力。框
    發(fā)表于 12-25 07:12

    無圖形界面模式下自定義檢查工具的應用

    此前文章已介紹 ANSA 中的自定義檢查工具。本文將探討該功能在無圖形界面(No-GUI)模式下的應用,旨在滿足標準化工作流程的需求,適用于需要高度自動化的前處理場景。通過集成自定義檢查,用戶可實現(xiàn)工作流程的高效自動化運行。
    的頭像 發(fā)表于 11-30 14:13 ?802次閱讀
    無圖形界面<b class='flag-5'>模式</b>下自<b class='flag-5'>定義</b>檢查工具的應用

    浮點擴展指令集中定義的五種舍入模式

    本文主要描述浮點擴展指令集中定義的五種舍入模式,并介紹一些實現(xiàn)時要注意的地方。 舍入模式介紹 首先,在riscv-spec-v2.2的浮點指令集擴展部分一共定義了五種不同的舍入
    發(fā)表于 10-24 10:25

    用LabVIEW開發(fā)的測試軟件,支持自定義測試內(nèi)容,分享大家。

    用LabVIEW開發(fā)的測試軟件,支持自定義測試內(nèi)容,分享大家。鏈接自取 鏈接: https://pan.baidu.com/s/14KtGsFmeFJ9ZkeVPygz2YQ?pwd=v8q7 提取碼: v8q7
    發(fā)表于 10-22 10:35

    從入門到精通:基于開源代碼的BLE四種模式開發(fā)詳解

    通過分析BLE低功耗藍牙的四種核心工作模式,結(jié)合可下載、可修改的開放源碼,本教程為開發(fā)提供一套系統(tǒng)、實用的開發(fā)學習路徑。 BLE(Bluetooth Low Energy): 也稱為
    的頭像 發(fā)表于 10-09 18:00 ?718次閱讀
    從入門到精通:基于開源代碼的BLE四種<b class='flag-5'>模式</b>開發(fā)詳解

    一文讀懂 RGB接口的 DE模式 和 行場(HV)模式 區(qū)別

    在了解RGB接口DE模式和行場(HV)模式 模式前,我們先看一下RGB接口屏幕的引腳定義: 下圖來之一款非常主流的工業(yè)級TFT的手冊引腳定義
    發(fā)表于 09-18 14:18

    CYPD3171自定義應用程序在接收模式下工作,但在源模式下不通告任何協(xié)議是怎么回事?

    我在自定義應用程序中結(jié)合使用 CYPD3171 芯片和 sc8815?;旧衔抑恍枰酒瑓f(xié)商電源角色、電壓和電流,然后將該信息發(fā)送給 MCU。我修改了示例移動電源代碼,禁用所有安全功能,禁用 USB
    發(fā)表于 07-22 08:21

    關(guān)于生命周期中的aboutToAppear和onPageShow的理解和應用

    過程、應用進入前臺等場景,僅@Entry裝飾的自定義組件作為頁面時生效。 從兩相同的角度來說,其都是在自定義組件顯示后,主動去觸發(fā)的生命周期,在這兩個生命周期里可以寫一些數(shù)據(jù)獲取啊等
    發(fā)表于 06-30 17:32

    AG32 SDK:加入DSP例程及支持boot_mode模式和自定義 Linker腳本等(v1.7.5版本)

    數(shù)據(jù)從 Flash 加載到 SRAM。這種方式雖然提升了運行時性能,但也帶來了更高的內(nèi)存占用。 引入的 flash_rodata 模式允許開發(fā)選擇將常量數(shù)據(jù)始終保留在 Flash 中,不再復制到
    發(fā)表于 05-20 14:14

    基于藍牙模組Beacon+觀察模式實現(xiàn)資產(chǎn)管理和室內(nèi)定位

    的一種廣播協(xié)議設備(從機)。Beacon主要參數(shù)①uuid②major③minor④companyID觀察模式1.用于監(jiān)聽其他設備的廣播數(shù)據(jù)而不與之建立連接;2.
    的頭像 發(fā)表于 05-15 19:34 ?1043次閱讀
    基于藍牙模組Beacon+觀察<b class='flag-5'>者</b><b class='flag-5'>模式</b>實現(xiàn)資產(chǎn)管理和室內(nèi)定位

    PCBA代工代料:定義、類型與精準選型指南

    時又該如何做出明智決策?本文將為您深入剖析。 一、PCBA代工代料:定義與內(nèi)涵 PCBA代工代料,顧名思義,是指企業(yè)將PCBA產(chǎn)品的制造任務,包括電路板(PCB)與電子元器件的采購、組裝等環(huán)節(jié),委托專業(yè)的代工廠商完成。這一模式
    的頭像 發(fā)表于 05-09 10:08 ?1512次閱讀
    静乐县| 岳普湖县| 海伦市| 梅河口市| 江门市| 星子县| 招远市| 鞍山市| 郑州市| 溧阳市| 高雄县| 富蕴县| 绥棱县| 百色市| 咸丰县| 车致| 溧水县| 岳普湖县| 鞍山市| 莒南县| 奇台县| 田阳县| 中江县| 松潘县| 花莲县| 蓝山县| 漳平市| 广昌县| 普兰店市| 瓮安县| 原阳县| 威海市| 淳化县| 安义县| 宜君县| 清镇市| 台北市| 庆元县| 泸州市| 武强县| 综艺|