Golang設計模式(四):觀察者模式

觀察者模式

什么是觀察者

觀察者模式(Observer Pattern):定義對象之間的一種一對多依賴關系,使得每當一個對象狀態發生改變時,其相關依賴對象皆得到通知并被自動更新。觀察者模式的別名包括發布-訂閱(Publish/Subscribe)模式、模型-視圖(Model/View)模式、源-監聽器(Source/Listener)模式或從屬者(Dependents)模式。觀察者模式是一種對象行為型模式。

結構

  1. **Subject(主題):**保持一個觀察者列表,提供添加、刪除和通知觀察者的方法。
  2. **Observer(觀察者):**定義一個更新接口,使得在主題狀態變化時得到通知。
  3. Concrete Subject(具體主題):實現Subject接口,存儲狀態,當狀態發生改變時,通知所有觀察者。
  4. Concrete Observer(具體觀察者):實現Observer接口,根據主題更新來更新自己的狀態。

基本流程

  1. 注冊觀察者:觀察者向主題注冊自己。
  2. 狀態變更:主題的狀態發生變化。
  3. 通知觀察者:主題通過調用注冊的觀察者的方法來通知它們狀態已變化。
  4. 更新觀察者:觀察者接收到通知后更新自己的狀態。

優點

  1. 解耦:觀察者模式能夠將主題和觀察者解耦,它們之間不需要知道對方的存在。
  2. 可擴展性:新增觀察者時,不需要修改主題的代碼,符合開閉原則。
  3. 動態交互:可以實現動態的交互,主題可以在運行時添加或刪除觀察者。

缺點

  1. 循環引用:如果不當使用,可能會導致循環引用,增加內存管理的難度。
  2. 性能問題:當觀察者較多時,通知所有觀察者可能會造成性能問題。
  3. 順序不確定:觀察者接收通知的順序是不確定的,可能會導致不可預知的副作用。

使用場景

觀察者模式通常用于構建松耦合的系統,其中一個對象(稱為主題或發布者)可以通知多個其他對象(稱為觀察者或訂閱者)關于狀態的變化。

  1. 在線購物平臺訂單管理
    • 主題(Subject):訂單系統,負責在訂單狀態更新時(如確認、發貨、收貨)廣播變更事件。
    • 觀察者(Observers):包括支付模塊、庫存管理、物流跟蹤等,它們監聽訂單狀態更新并執行相應操作。
  2. 圖形用戶界面(GUI)同步
    • 主題(Subject):文檔管理系統,監控文檔內容的更改并觸發更新事件。
    • 觀察者(Observers):界面組件如文本框、滾動條、狀態欄等,它們接收更新事件并刷新顯示。
  3. 模型-視圖-控制器(MVC)架構
    • 主題(Subject):數據模型,實時更新數據狀態并通知視圖與控制器。
    • 觀察者(Observers):視圖界面和控制器邏輯,訂閱數據變更,視圖更新顯示,控制器響應用戶交互。
  4. 社交媒體內容更新
    • 主題(Subject):用戶發布系統,當用戶發布新推文或狀態時觸發通知。
    • 觀察者(Observers):粉絲和關注者,他們接收到新內容的通知并更新自己的信息流。
  5. 股票交易實時系統
    • 主題(Subject):股票行情中心,實時監控并發布股票價格的變動。
    • 觀察者(Observers):交易平臺界面、分析工具、自動交易腳本等,它們根據行情變化進行決策和操作。
  6. 動態配置更新系統
    • 主題(Subject):配置服務器,負責維護應用配置并在配置更新時發送通知。
    • 觀察者(Observers):應用服務和組件,它們監聽配置變更并實時調整自身設置。

注意事項

  1. 避免循環引用:確保主題和觀察者之間不會產生循環引用。
  2. 管理生命周期:合理管理主題和觀察者的生命周期,避免內存泄漏。
  3. 線程安全:在多線程環境中使用觀察者模式時,需要考慮線程安全問題

代碼案例

package designpatternimport ("fmt""sync"
)// Observer 觀察者接口
type Observer interface {Update() // Update方法用于接收主題狀態變化的通知
}// ConcreteObserver 具體觀察者
type ConcreteObserver struct {name string
}func (c *ConcreteObserver) Update() {fmt.Printf("%s is notified.\n", c.name) // 具體觀察者接收到通知后的具體處理邏輯
}// Subject 主題接口
type Subject interface {RegisterObserver(observer Observer)    // 注冊觀察者DeregisterObserver(observer Observer)  // 注銷觀察者NotifyObservers()                      // 通知所有觀察者
}// ConcreteSubject 具體主題
type ConcreteSubject struct {observers []Observer // 觀察者列表state     int        // 主題狀態mu        sync.Mutex // 互斥鎖,用于保護并發訪問
}// NewConcreteSubject 創建具體主題實例
func NewConcreteSubject() *ConcreteSubject {return &ConcreteSubject{observers: make([]Observer, 0),mu:        sync.Mutex{}, // 初始化互斥鎖}
}func (cs *ConcreteSubject) RegisterObserver(observer Observer) {cs.mu.Lock()defer cs.mu.Unlock()cs.observers = append(cs.observers, observer) // 注冊觀察者到列表中
}func (cs *ConcreteSubject) DeregisterObserver(observer Observer) {cs.mu.Lock()defer cs.mu.Unlock()for i, ob := range cs.observers {if ob == observer {cs.observers = append(cs.observers[:i], cs.observers[i+1:]...) // 從觀察者列表中注銷觀察者break}}
}func (cs *ConcreteSubject) NotifyObservers() {cs.mu.Lock()defer cs.mu.Unlock()for _, ob := range cs.observers {ob.Update() // 通知所有觀察者主題狀態變化}
}func (cs *ConcreteSubject) SetState(state int) {cs.mu.Lock()defer cs.mu.Unlock()cs.state = state // 設置主題狀態cs.NotifyObservers() // 通知所有觀察者主題狀態變化
}func main() {// 在 main 函數中演示了具體的使用方法,創建具體主題實例,注冊觀察者,并設置主題狀態,觸發通知subject := NewConcreteSubject()ob1 := &ConcreteObserver{"ob1"}ob2 := &ConcreteObserver{"ob2"}subject.RegisterObserver(ob1)subject.RegisterObserver(ob2)subject.SetState(1)
}

模擬一個新聞發布網站

package mainimport ("fmt""sync"
)// 新聞類型
type NewsType intconst (Business NewsType = iotaTechnologySportsWorldEntertainment
)// 觀察者接口
type Observer interface {Update(News)
}// 具體觀察者結構體
type Subscriber struct {Name       stringInterests  map[NewsType]boolRegister   chan NewsTypeUnregister chan NewsType
}func NewSubscriber(name string) *Subscriber {return &Subscriber{Name:       name,Interests:  make(map[NewsType]bool),Register:   make(chan NewsType),Unregister: make(chan NewsType),}
}func (s *Subscriber) Update(news News) {if _, ok := s.Interests[news.Type]; ok {fmt.Printf("%s received news: %s\n", s.Name, news.Headline)}
}func (s *Subscriber) RegisterInterest(interest NewsType) {s.Register <- interests.Interests[interest] = true
}func (s *Subscriber) UnregisterInterest(interest NewsType) {s.Unregister <- interestdelete(s.Interests, interest)
}// 主題接口
type Subject interface {Attach(Observer)Detach(Observer)Notify(string)
}// 具體主題結構體
type NewsAgency struct {observers map[Observer]boolnews       chan Newsmu         sync.Mutex
}func NewNewsAgency() *NewsAgency {return &NewsAgency{observers: make(map[Observer]bool),news:      make(chan News),}
}func (a *NewsAgency) Attach(observer Observer) {a.mu.Lock()defer a.mu.Unlock()a.observers[observer] = true
}func (a *NewsAgency) Detach(observer Observer) {a.mu.Lock()defer a.mu.Unlock()delete(a.observers, observer)
}func (a *NewsAgency) Notify(headline string) {for observer, _ := range a.observers {news := News{Headline: headline}go observer.Update(news)}
}// 新聞結構體
type News struct {Headline stringType     NewsType
}func main() {// 創建新聞機構agency := NewNewsAgency()// 創建訂閱者alice := NewSubscriber("Alice")bob := NewSubscriber("Bob")// 訂閱興趣alice.RegisterInterest(Business)alice.RegisterInterest(World)bob.RegisterInterest(Technology)bob.RegisterInterest(Entertainment)// 將訂閱者作為觀察者注冊到新聞機構agency.Attach(alice)agency.Attach(bob)// 新聞發布agency.Notify("Big Corp acquired Small Tech for $1B")// 訂閱者取消訂閱bob.UnregisterInterest(Entertainment)// 再次新聞發布agency.Notify("New breakthrough in AI technology")
}
  • 定義了 NewsType 類型,用于區分不同類型的新聞。
  • Observer 接口有一個 Update 方法,用于接收新聞更新。
  • Subscriber 結構體代表具體的觀察者,它包含訂閱者的名字和興趣,以及注冊和注銷興趣的通道。
  • Subject 接口包含 AttachDetachNotify 方法。
  • NewsAgency 結構體代表具體的主題,它維護了一個觀察者集合和一個發布新聞的通道。
  • News 結構體包含新聞的標題和類型。
  • main 函數中,我們創建了新聞機構和兩個訂閱者,將訂閱者的興趣注冊到新聞機構,并模擬了新聞發布。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/16124.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/16124.shtml
英文地址,請注明出處:http://en.pswp.cn/web/16124.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

音視頻開發8 音視頻中SDL的使用,SDL 在windows上環境搭建,SDL 使用 以及 常用 API說明,show YUV and play PCM

1.SDL簡介 SDL&#xff08;Simple DirectMedia Layer&#xff09;&#xff0c;是一個跨平臺的C語言多媒體開發庫。 支持Windows、Mac OS X、Linux、iOS、Android 提供對音頻、鍵盤、鼠標、游戲操縱桿、圖形硬件的底層訪問 很多的視頻播放軟件、模擬器、受歡迎的游戲都在使用…

面試中算法(A星尋路算法)

一、問題需求&#xff1a; 迷宮尋路游戲中&#xff0c;有一些小怪物要攻擊主角&#xff0c;現在希望你給這些小怪物加上聰 明的AI (Artificial Intelligence&#xff0c;人工智能&#xff09;&#xff0c;讓它們可以自動繞過迷宮中的障礙物&#xff0c;尋找到主角的所在。 A星…

json web token及JWT學習與探索

JSON Web Token&#xff08;縮寫 JWT&#xff09;是目前最流行的跨域認證解決方案 作用&#xff1a; 主要是做鑒權用的登錄之后存儲用戶信息 生成得token(令牌)如下 eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwiaWF0IjoxNjg3Njc0NDkyLCJleHAiOjE2ODc3NjA4OTJ9.Y6eFG…

Django使用fetch實現登錄

Django使用session管理&#xff08;cookie&#xff09;實現了一個用戶登錄和會話保持功能。如果需求不太復雜可以使用Django默認的登錄功能。 1 安裝django-cors-headers 首先需要安裝django-cors-headers pip install django-cors-headers2 在settings中配置 需要按照djan…

用Dockerfile和Shell腳本來部署一個Go項目

如何使用Dockerfile和Shell腳本來部署一個Go項目。這種方法能夠幫助我們自動化構建、測試和部署流程&#xff0c;提高開發效率。 **一、項目結構和代碼** 首先&#xff0c;我們需要準備一個Go項目。假設我們的項目結構如下&#xff1a; my-go-app/ ├── main.go ├── D…

1107 老鼠愛大米

solution 記錄每組的最大值&#xff0c;并比較組間的最大值胖胖鼠~ #include<iostream> using namespace std; int main(){int n, m, ans, fat -1, x;scanf("%d%d", &n, &m);for(int i 0; i < n; i){ans -1;for(int j 0; j < m; j){scanf(…

【C/C++】Makefile文件的介紹與基本用法

創作不易&#xff0c;本篇文章如果幫助到了你&#xff0c;還請點贊 關注支持一下?>&#x16966;<)!! 主頁專欄有更多知識&#xff0c;如有疑問歡迎大家指正討論&#xff0c;共同進步&#xff01; &#x1f525;c系列專欄&#xff1a;C/C零基礎到精通 &#x1f525; 給大…

第三周:從錯誤中認識到管理

1. 約定兩周時間&#xff0c;完成這個功能 在管理者分配好項目任務后&#xff0c;只是口頭約定兩周的時間&#xff0c;沒有形成需求文檔。對于需求&#xff0c;人與人的理解是不一樣的&#xff0c;有些太過于抽象的東西&#xff0c;太難以描繪&#xff0c;只能一而再再而三的確…

【論文復現】LSTM長短記憶網絡

LSTM 前言網絡架構總線遺忘門記憶門記憶細胞輸出門 模型定義單個LSTM神經元的定義LSTM層內結構的定義 模型訓練模型評估代碼細節LSTM層單元的首尾的處理配置Tensorflow的GPU版本 前言 LSTM作為經典模型&#xff0c;可以用來做語言模型&#xff0c;實現類似于語言模型的功能&am…

vue3的proxy如何取代object和defineproperty

在 Vue 2.x 中&#xff0c;為了響應式地追蹤對象屬性的變化&#xff0c;Vue 使用了 Object.defineProperty 方法。但是&#xff0c;Object.defineProperty 有一些限制&#xff0c;比如它不能追蹤屬性的添加或刪除&#xff0c;也不能直接用于數組或對象原型鏈上的屬性。 Vue 3.…

【Torch學習筆記】

作者&#xff1a;zjk 和 的區別是逐元素相乘&#xff0c;是矩陣相乘 cat stack 的區別 cat stack 是用于沿新維度將多個張量堆疊在一起的函數。它要求所有輸入張量具有相同的形狀&#xff0c;并在指定的新維度上進行堆疊。

【NumPy】關于numpy.mean()函數,看這一篇文章就夠了

&#x1f9d1; 博主簡介&#xff1a;阿里巴巴嵌入式技術專家&#xff0c;深耕嵌入式人工智能領域&#xff0c;具備多年的嵌入式硬件產品研發管理經驗。 &#x1f4d2; 博客介紹&#xff1a;分享嵌入式開發領域的相關知識、經驗、思考和感悟&#xff0c;歡迎關注。提供嵌入式方向…

Android11熱點啟動和關閉

Android官方關于Wi-Fi Hotspot (Soft AP) 的文章&#xff1a;https://source.android.com/docs/core/connect/wifi-softap?hlzh-cn 在 Android 11 的WifiManager類中有一套系統 API 可以控制熱點的開和關&#xff0c;代碼如下&#xff1a; 開啟熱點&#xff1a; // SoftApC…

Vue 父組件使用refs來直接訪問和修改子組件的屬性或調用子組件的方法

步驟 1: 在子組件中定義要被修改的屬性或方法 首先&#xff0c;在子組件中定義你想要父組件能夠修改或調用的屬性或方法。例如&#xff0c;我們有一個名為MyChildComponent的子組件&#xff0c;它有一個名為childData的數據屬性和一個名為updateData的方法。 // 子組件 MyChi…

國際版Tiktok抖音運營流量實戰班:賬號定位/作品發布/熱門推送/等等-13節

課程目錄 1-tiktok賬號定位 1.mp4 2-tiktok作品發布技巧 1.mp4 3-tiktok數據功能如何開通 1.mp4 4-tiktok熱門視頻推送機制 1.mp4 5-如何發現熱門視頻 1.mp4 6-如何發現熱門音樂 1.mp4 7-如何尋找熱門標簽 1.mp4 8-如何尋找垂直熱門視頻 1.mp4 9-如何發現熱門挑戰賽 1…

【Python特征工程系列】一文教你使用PCA進行特征分析與降維(案例+源碼)

這是我的第287篇原創文章。 一、引言 主成分分析&#xff08;Principal Component Analysis, PCA&#xff09;是一種常用的降維技術&#xff0c;它通過線性變換將原始特征轉換為一組線性不相關的新特征&#xff0c;稱為主成分&#xff0c;以便更好地表達數據的方差。 在特征重要…

DAMA數據管理知識體系必背18張框圖

近期對數據管理知識體系中比較重要的框圖進行了梳理總結,總共有18張框圖,供大家參考。主要涉及數據管理、數據治理階段模式、數據安全需求、主數據管理關鍵步驟,主數據架構、DW架構、數據科學的7個階段、數據倉庫建設活動、信息收斂三角、大數據分析架構圖、數據管理成熟度等…

QGIS開發筆記(二):Windows安裝版二次開發環境搭建(上):安裝OSGeo4W運行依賴其Qt的基礎環境Demo

若該文為原創文章&#xff0c;轉載請注明原文出處 本文章博客地址&#xff1a;https://hpzwl.blog.csdn.net/article/details/139136356 長沙紅胖子Qt&#xff08;長沙創微智科&#xff09;博文大全&#xff1a;開發技術集合&#xff08;包含Qt實用技術、樹莓派、三維、OpenCV…

如果返回的json 中有 ‘///’ 轉換

// 將返回數據的三條/和替換空 rowData.Jsonobj rowData.Jsonobj .replace(/^\s*\/\/\/.*$/gm, //); // 將返回的替換成" 并且外面加個"" rowData.Jsonobj "${rowData.Jsonobj .replace(//g, ")}"; // 轉換回來數據用兩個 JSON.parse(JSON.par…

Charles抓包App_https_夜神模擬器

Openssl安裝 下載安裝 下載地址&#xff1a; http://slproweb.com/products/Win32OpenSSL.html 我已經下載好了64位的&#xff0c;也放出來&#xff1a; 鏈接&#xff1a;https://pan.baidu.com/s/1Nkur475YK48_Ayq_vEm99w?pwdf4d7 提取碼&#xff1a;f4d7 --來自百度網…