Go設計模式(18)-觀察者模式

終於寫完了建立型和結構型設計模式(共12個),現在開始寫行為型設計模式(共11個)。觀察者模式的應用場景非常廣泛,小到程式碼層面的解耦,大到架構層面的系統解耦,再或者一些產品的設計思路,都有這種模式的影子。

UML類圖位置:https://www。processon。com/view/link/60d29bf3e401fd49502afd25

本文程式碼連結為:https://github。com/shidawuhen/asap/blob/master/controller/design/18observer。go

1。定義

1。1觀察者模式

觀察者模式

:觀察者模式定義了一種一對多的依賴關係,讓多個觀察者物件同時監聽某一個主題物件。這個主題物件在狀態發生變化時,會通知所有觀察者物件,使他們能夠自動更新自己。

UML

Go設計模式(18)-觀察者模式

1。2分析

觀察者模式算是比較通用的設計模式了,思路也比較清晰。Subject裡有Observer集合,當Subject有更新時,通知各個Observer。Subject為了管理Observer,自身設定了增加、刪除功能。

當Subject有更新,通知Observer時,有很多細節值得討論。

第一個問題是使用同步阻塞還是非同步非阻塞

同步阻塞是最經典的實現方式,主要是為了程式碼解耦;

非同步非阻塞除了能實現程式碼解耦之外,還能提高程式碼的執行效率;

第二個問題是如何保證所有Observer都通知成功。

方案一是利用訊息佇列ACK的能力,Observer訂閱訊息佇列。Subject只需要確保資訊通知給訊息佇列即可。

方案二是Subject將失敗的通知記錄,方便後面進行重試。

方案三是定義好規範,例如只對網路失敗這種錯誤進行記錄,業務失敗型別不管理,由業務自行保證成功。

第三個問題是不同程序/系統如何進行通知。

程序間的觀察者模式解耦更加徹底,一般是基於訊息佇列來實現,用來實現不同程序間的被觀察者和觀察者之間的互動。

2。使用場景

觀察者模式的使用場景還是很多的,它和享元模式一樣,更多體現的是一種設計理念。

記得前些日子遇到一個場景,正好使用的是觀察者模式。商家購買服務的訂單,有多個狀態,如交易成功、交易取消、開始履約等。業務場景只關注交易成功、交易取消和開始履約。如果不使用觀察者模式,可以用if-else解決這個問題。但各個狀態的處理邏輯比較複雜,而且狀態有可能存在亂序的情況,導致要處理的case更多了。這時候用觀察者模式就很合適,訂單狀態變更為Subject,具體處理邏輯為Observer,狀態變更時,通知所有處理邏輯,誰合適處理便由誰處理。不但隔離性變好,擴充套件性也增強了。

3。程式碼實現

package mainimport “fmt”type PurchaseOperFunc func(status string, data string) (res bool, err error)/** * @Author: Jason Pang * @Description: 註冊的觀察者 */var PurchaseOperFuncArr = []PurchaseOperFunc{ create, isDeleted, apply,}/** * @Author: Jason Pang * @Description: 用於建立的觀察者 * @param status * @param data * @return res * @return err */func create(status string, data string) (res bool, err error) { if status == “create” { fmt。Println(“開始建立”) return true, nil } return true, nil}/** * @Author: Jason Pang * @Description: 用於刪除的觀察者 * @param status * @param data * @return res * @return err */func isDeleted(status string, data string) (res bool, err error) { if status == “delete” { fmt。Println(“開始刪除”) return true, nil } return true, nil}/** * @Author: Jason Pang * @Description: 用於履約的觀察者 * @param status * @param data * @return res * @return err */func apply(status string, data string) (res bool, err error) { if status == “apply” { fmt。Println(“開始履約”) return true, nil } return true, nil}func main() { status := “create” data := “訂單資料” //有狀態更新時,通知所有觀察者 for _, oper := range PurchaseOperFuncArr { res, err := oper(status, data) if err != nil { fmt。Println(“操作失敗”) break } if res == false { fmt。Println(“處理失敗”) break } }}

輸出:

➜ myproject go run main。go

開始建立

這個程式碼是個簡單版,但整體邏輯是一樣的。這種寫法的好處有如下幾個:

如果要處理新的狀態,則實現後再PurchaseOperFuncArr新增即可,改動不大

根據具體業務來說,只能有一個邏輯是真正負責執行的,所以有一個錯誤就報錯,然後重試即可。不過具體邏輯需要做好冪等

總結

實際上,設計模式要乾的事情就是解耦。建立型模式是將建立和使用程式碼解耦,結構型模式是將不同功能程式碼解耦,行為型模式是將不同的行為程式碼解耦,具體到觀察者模式,它是將觀察者和被觀察者程式碼解耦。

最後

大家如果喜歡我的文章,可以關注我的公眾號(程式設計師麻辣燙)

我的個人部落格為:https://shidawuhen。github。io/

往期文章回顧:

招聘

位元組跳動|內推大放送

位元組跳動|今日頭條廣州服務端研發工程師內推

位元組跳動|抖音電商急招上海前端開發工程

位元組跳動|抖音電商上海資深服務端開發工程師-交易

位元組跳動|抖音電商武漢服務端(高階)開發工程師

位元組跳動|飛書大客戶產品經理內推咯

位元組跳動|抖音電商服務端技術崗位虛位以待

位元組跳動招聘專題

設計模式

Go設計模式(17)-享元模式

Go設計模式(16)-組合模式

Go設計模式(15)-門面模式

Go設計模式(14)-介面卡模式

Go設計模式(13)-裝飾器模式

Go設計模式(12)-橋接模式

Go設計模式(11)-代理模式

Go設計模式(10)-原型模式

Go設計模式(9)-建造者模式

Go設計模式(8)-抽象工廠

Go設計模式(7)-工廠模式

Go設計模式(6)-單例模式

Go設計模式(5)-類圖符號表示法

Go設計模式(4)-程式碼編寫最佳化

Go設計模式(4)-程式碼編寫

Go設計模式(3)-設計原則

Go設計模式(2)-面向物件分析與設計

Go設計模式(1)-語法

語言

再也不怕獲取不到Gin請求資料了

一文搞懂pprof

Go工具之generate

Go單例實現方案

Go通道實現原理

Go定時器實現原理

Beego框架使用

Golang原始碼BUG追查

Gin框架簡潔版

Gin原始碼剖析

架構

分頁複選設計的坑

支付接入常規問題

限流實現2

秒殺系統

分散式系統與一致性協議

微服務之服務框架和註冊中心

淺談微服務

限流實現1

CDN請求過程詳解

常用快取技巧

如何高效對接第三方支付

演算法總結

儲存

一文搞懂MySQL資料庫分庫分表

MySQL開發規範

Redis實現分散式鎖

事務原子性、一致性、永續性的實現原理

InnoDB鎖與事務簡析

網路

HTTP2。0基礎教程

HTTPS配置實戰

HTTPS連線過程

TCP效能最佳化

工具

GoLand實用技巧

根據mysql表自動生成go struct

Markdown編輯器推薦-typora

讀書筆記

《毛選》推薦

原則

資治通鑑

敏捷革命

如何鍛鍊自己的記憶力

簡單的邏輯學-讀後感

熱風-讀後感

論語-讀後感

孫子兵法-讀後感

思考

對過去的一點回顧

對寫部落格的一些思考

晚上打119的經歷

為動員一切力量爭取勝利而鬥爭

反對自由主義

實踐論

評價自己的標準

2020部落格總結

服務端團隊假期值班方案

專案流程管理

對專案管理的一些看法

對產品經理的一些思考

關於程式設計師職業發展的思考

關於程式碼review的思考