日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
觀察者模式的實(shí)際應(yīng)用

本文轉(zhuǎn)載自微信公眾號「crossoverJie」,作者crossoverJie。轉(zhuǎn)載本文請聯(lián)系crossoverJie公眾號。

公司主營業(yè)務(wù):成都網(wǎng)站建設(shè)、網(wǎng)站設(shè)計(jì)、移動(dòng)網(wǎng)站開發(fā)等業(yè)務(wù)。幫助企業(yè)客戶真正實(shí)現(xiàn)互聯(lián)網(wǎng)宣傳,提高企業(yè)的競爭能力。成都創(chuàng)新互聯(lián)是一支青春激揚(yáng)、勤奮敬業(yè)、活力青春激揚(yáng)、勤奮敬業(yè)、活力澎湃、和諧高效的團(tuán)隊(duì)。公司秉承以“開放、自由、嚴(yán)謹(jǐn)、自律”為核心的企業(yè)文化,感謝他們對我們的高要求,感謝他們從不同領(lǐng)域給我們帶來的挑戰(zhàn),讓我們激情的團(tuán)隊(duì)有機(jī)會用頭腦與智慧不斷的給客戶帶來驚喜。成都創(chuàng)新互聯(lián)推出寧海免費(fèi)做網(wǎng)站回饋大家。

前言

設(shè)計(jì)模式不管是在面試還是工作中都會遇到,但我經(jīng)常碰到小伙伴抱怨實(shí)際工作中自己應(yīng)用設(shè)計(jì)模式的機(jī)會非常小。

正好最近工作中遇到一個(gè)用觀察者模式解決問題的場景,和大家一起分享。

背景如下:

在用戶創(chuàng)建完訂單的標(biāo)準(zhǔn)流程中需要做額外一些事情:

同時(shí)這些業(yè)務(wù)也是不固定的,隨時(shí)會根據(jù)業(yè)務(wù)發(fā)展增加、修改邏輯。

如果直接將邏輯寫在下單業(yè)務(wù)中,這一”坨“不是很核心的業(yè)務(wù)就會占據(jù)的越來越多,修改時(shí)還有可能影響到正常的下單流程。

當(dāng)然也有其他方案,比如可以啟動(dòng)幾個(gè)定時(shí)任務(wù),定期掃描掃描訂單然后實(shí)現(xiàn)自己的業(yè)務(wù)邏輯;但這樣會浪費(fèi)許多不必要的請求。

觀察者模式

因此觀察者模式就應(yīng)運(yùn)而生,它是由事件發(fā)布者在自身狀態(tài)發(fā)生變化時(shí)發(fā)出通知,由觀察者獲取消息實(shí)現(xiàn)業(yè)務(wù)邏輯。

這樣事件發(fā)布者和接收者就可以完全解耦,互不影響;本質(zhì)上也是對開閉原則的一種實(shí)現(xiàn)。

示例代碼

先大體看一下觀察者模式所使用到的接口與關(guān)系:

  • 主體接口:定義了注冊實(shí)現(xiàn)、循環(huán)通知接口。
  • 觀察者接口:定義了接收主體通知的接口。
  • 主體、觀察者接口都可以有多個(gè)實(shí)現(xiàn)。
  • 業(yè)務(wù)代碼只需要使用 Subject.Nofity() 接口即可。

接下來看看創(chuàng)建訂單過程中的實(shí)現(xiàn)案例。

代碼采用 go 實(shí)現(xiàn),其他語言也是類似。

首先按照上圖定義了兩個(gè)接口:

  
 
 
 
  1. type Subject interface { 
  2.  Register(Observer) 
  3.  Notify(data interface{}) 
  4.  
  5. type Observer interface { 
  6.  Update(data interface{}) 

由于我們這是一個(gè)下單的事件,所以定義了 OrderCreateSubject 實(shí)現(xiàn) Subject:

  
 
 
 
  1. type OrderCreateSubject struct { 
  2.  observerList []Observer 
  3.  
  4. func NewOrderCreate() Subject { 
  5.  return &OrderCreateSubject{} 
  6.  
  7. func (o *OrderCreateSubject) Register(observer Observer) { 
  8.  o.observerList = append(o.observerList, observer) 
  9. func (o *OrderCreateSubject) Notify(data interface{}) { 
  10.  for _, observer := range o.observerList { 
  11.   observer.Update(data) 
  12.  } 

其中的 observerList 切片是用于存放所有訂閱了下單事件的觀察者。

接著便是編寫觀察者業(yè)務(wù)邏輯了,這里我實(shí)現(xiàn)了兩個(gè):

  
 
 
 
  1. type B1CreateOrder struct { 
  2. func (b *B1CreateOrder) Update(data interface{}) { 
  3.  fmt.Printf("b1.....data %v \n", data) 
  4.  
  5.  
  6. type B2CreateOrder struct { 
  7. func (b *B2CreateOrder) Update(data interface{}) { 
  8.  fmt.Printf("b2.....data %v \n", data) 

使用起來也非常簡單:

  
 
 
 
  1. func TestObserver(t *testing.T) { 
  2.  create := NewOrderCreate() 
  3.  create.Register(&B1CreateOrder{}) 
  4.  create.Register(&B2CreateOrder{}) 
  5.  
  6.  create.Notify("abc123") 

Output:

  
 
 
 
  1. b1.....data abc123 
  2.  
  3. b2.....data abc123 
  1. 創(chuàng)建一個(gè)創(chuàng)建訂單的主體 subject。
  2. 注冊所有的訂閱事件。
  3. 在需要通知處調(diào)用 Notify 方法。

這樣一旦我們需要修改各個(gè)事件的實(shí)現(xiàn)時(shí)就不會互相影響,即便是要加入其他實(shí)現(xiàn)也是非常容易的:

  1. 編寫實(shí)現(xiàn)類。
  2. 注冊進(jìn)實(shí)體。

不會再修改核心流程。

配合容器

其實(shí)我們也可以省略掉注冊事件的步驟,那就是使用容器;大致流程如下:

自定義的事件全部注入進(jìn)容器。

再注冊事件的地方從容器中取出所有的事件,挨個(gè)注冊。

這里所使用的容器是 https://github.com/uber-go/dig

修改后的代碼中,每當(dāng)我們新增一個(gè)觀察者(事件訂閱)時(shí),只需要使用容器所提供 Provide 函數(shù)注冊進(jìn)容器即可。

同時(shí)為了讓容器能夠支持同一個(gè)對象存在多個(gè)實(shí)例也需要新增部分代碼:

  
 
 
 
  1. type Observer interface { 
  2.  Update(data interface{}) 
  3. type ( 
  4.  Instance struct { 
  5.   dig.Out 
  6.   Instance Observer `group:"observers"` 
  7.  } 
  8.  
  9.  InstanceParams struct { 
  10.   dig.In 
  11.   Instances []Observer `group:"observers"` 
  12.  } 

在 observer 接口中需要新增兩個(gè)結(jié)構(gòu)體用于存放同一個(gè)接口的多個(gè)實(shí)例。

group:"observers" 用于聲明是同一個(gè)接口。

創(chuàng)建具體觀察者對象時(shí)返回 Instance 對象。

  
 
 
 
  1. func NewB1() Instance { 
  2.  return Instance{ 
  3.   Instance: &B1CreateOrder{}, 
  4.  } 
  5.  
  6. func NewB2() Instance { 
  7.  return Instance{ 
  8.   Instance: &B2CreateOrder{}, 
  9.  } 

其實(shí)就是用 Instance 包裝了一次。

這樣在注冊觀察者時(shí),便能從 InstanceParams.Instances 中取出所有的觀察者對象了。

  
 
 
 
  1. err = c.Invoke(func(subject Subject, params InstanceParams) { 
  2.  for _, instance := range params.Instances { 
  3.   subject.Register(instance) 
  4.  } 
  5. }) 

這樣在使用時(shí)直接從容器中獲取主題對象,然后通知即可:

  
 
 
 
  1. err = c.Invoke(func(subject Subject) { 
  2.  subject.Notify("abc123") 
  3. }) 

更多關(guān)于 dig 的用法可以參考官方文檔:

https://pkg.go.dev/go.uber.org/dig#hdr-Value_Groups

總結(jié)

有經(jīng)驗(yàn)的開發(fā)者會發(fā)現(xiàn)和發(fā)布訂閱模式非常類似,當(dāng)然他們的思路是類似的;我們不用糾結(jié)與兩者的差異(面試時(shí)除外);學(xué)會其中的思路更加重要。


文章名稱:觀察者模式的實(shí)際應(yīng)用
URL鏈接:http://www.5511xx.com/article/dhijspi.html