設(shè)計(jì)模式之觀察者模式
應(yīng)用案例
大多數(shù)人都有過網(wǎng)上搶購商品的經(jīng)歷,以淘寶的“淘搶購”為例。買家想要在22點(diǎn)搶購襯衫,點(diǎn)擊“提醒我”按鈕。那么淘寶就會(huì)在開搶之前,及時(shí)把搶購消息推送給買家。

該消息提醒推送的實(shí)現(xiàn),就是經(jīng)典的觀察者模式。

代碼實(shí)現(xiàn)
UML類圖

創(chuàng)建Subject接口
1package?observer
2
3type?Subject?interface?{
4????Register(o?Observer)
5????Deregister(o?Observer)?error
6????NotifyObservers()
7}
創(chuàng)建Observer接口
1package?observer
2
3type?Observer?interface?{
4????Update(name,?status?string)
5????GetID()?int
6}
創(chuàng)建shirt對(duì)象,實(shí)現(xiàn)Subjuect接口
1package?observer
2
3import?(
4????"fmt"
5????"sync"
6)
7
8const?(
9????TimeIsUp??=?"time?is?up"
10????IsEnd?????=?"is?end"
11????NotAtTime?=?"not?at?time"
12)
13
14type?shirt?struct?{
15????sync.Mutex
16????customers?[]Observer
17????status????string
18????name??????string
19}
20
21//NewShirt?create?a?shirt,?its?default?status?is?not?at?time.
22func?NewShirt()?*shirt?{
23????return?&shirt{status:?NotAtTime,?name:?"shirt"}
24}
25
26//?Register?registers?the?observer?(customer)?of?this?shirt.
27func?(s?*shirt)?Register(o?Observer)?{
28????s.Lock()
29????defer?s.Unlock()
30????s.customers?=?append(s.customers,?o)
31????fmt.Printf("[%s]?registered?a?new?customer?with?ID[%d]\n",?s.name,?o.GetID())
32}
33
34//?Deregister?removes?the?observer?(customer)?from?the?customers?list.
35//?The?removed?observer?(customer)?won't?get?notifications?any?more.
36func?(s?*shirt)?Deregister(o?Observer)?error?{
37????s.Lock()
38????defer?s.Unlock()
39????var?index?int
40????var?found?bool
41????id?:=?o.GetID()
42????for?i,?c?:=?range?s.customers?{
43????????if?c.GetID()?==?id?{
44????????????index?=?i
45????????????found?=?true
46????????????break
47????????}
48????}
49????if?!found?{
50????????return?fmt.Errorf("Customer?%d?not?found\n",?id)
51????}
52????s.customers?=?append(s.customers[:index],?s.customers[index+1:]...)
53????fmt.Printf("Removed?the?customer?with?ID[%d]\n",?id)
54????return?nil
55}
56
57//?NotifyObservers?notifies?the?customers?(customers)?when?the?shirt?has?in?status?"time?is?up".
58func?(s?*shirt)?NotifyObservers()?{
59????s.Lock()
60????defer?s.Unlock()
61????wg?:=?sync.WaitGroup{}
62????for?_,?c?:=?range?s.customers?{
63????????wg.Add(1)
64????????go?func(c?Observer)?{
65????????????defer?wg.Done()
66????????????c.Update(s.name,?s.status)
67????????}(c)
68????}
69????wg.Wait()
70????fmt.Println("Finished?notify?customers")
71}
創(chuàng)建customer對(duì)象,實(shí)現(xiàn)Observer接口
1package?observer
2
3import?"fmt"
4
5type?customer?struct?{
6????ID?????????????int
7????wantItemStatus?string
8}
9
10//?NewCustomer?creates?a?new?customer?with?an?ID
11func?NewCustomers(id?int)?*customer?{
12????return?&customer{ID:?id}
13}
14
15//?Update?function?updates?the?item?status?of?the?customer's?want.
16func?(c?*customer)?Update(name,?status?string)?{
17????c.wantItemStatus?=?status
18????fmt.Printf("Update:?hi?customer?%d,?the?item[%s]?you?want?is?[%v]?now\n",?c.ID,?name,?c.wantItemStatus)
19}
20
21//?GetID?returns?the?ID?of?the?customer.
22func?(c?customer)?GetID()?int?{
23????return?c.ID
24}
整合測(cè)試
1package?observer
2
3import?(
4????"fmt"
5????"testing"
6)
7
8//?UpdateShirtStatusForTest?updates?the?status?of?a?shirt.
9//?This?function?is?for?testing?only.
10func?(s?*shirt)?UpdateShirtStatusForTest(status?string)?{
11????if?s.status?!=?status?{
12????????fmt.Printf("Update?status?of?the?[shirt]:?previous?is?[%s],?current?is?[%s]\n",?s.status,?status)
13????????s.status?=?status
14????}
15}
16func?TestRushToBuy(t?*testing.T)?{
17????c1?:=?NewCustomers(1)
18????c2?:=?NewCustomers(2)
19????c3?:=?NewCustomers(3)
20
21????s?:=?NewShirt()
22????s.Register(c1)
23????s.Register(c2)
24????s.Register(c3)
25
26????s.UpdateShirtStatusForTest(TimeIsUp)
27????s.NotifyObservers()
28}
測(cè)試結(jié)果
1===?RUN???TestRushToBuy
2[shirt]?registered?a?new?customer?with?ID[1]
3[shirt]?registered?a?new?customer?with?ID[2]
4[shirt]?registered?a?new?customer?with?ID[3]
5Update?status?of?the?[shirt]:?previous?is?[not?at?time],?current?is?[time?is?up]
6Update:?hi?customer?1,?the?item[shirt]?you?want?is?[time?is?up]?now
7Update:?hi?customer?2,?the?item[shirt]?you?want?is?[time?is?up]?now
8Update:?hi?customer?3,?the?item[shirt]?you?want?is?[time?is?up]?now
9Finished?notify?customers
10---?PASS:?TestRushToBuy?(0.00s)
11PASS總結(jié)
改變主題或觀察者其中一方,并不會(huì)影響到另一方。只要他們之間的接口仍被遵守,我們就可以自由地改變他們。松耦合的設(shè)計(jì)能讓我們建立富有彈性的OO系統(tǒng)。
1. 任何一種設(shè)計(jì)模式都不可能是完美無瑕的。松耦合的設(shè)計(jì)機(jī)制,將主題與觀察者抽象在了不同的層次。但是這樣的設(shè)計(jì)使得觀察者模式?jīng)]有相應(yīng)的機(jī)制讓觀察者知道主題對(duì)象是怎么發(fā)生變化的,而僅僅只是知道主題發(fā)生了變化。
2. 另外,當(dāng)一個(gè)主題對(duì)象有很多觀察者時(shí),將所有的觀察者都通知到可能會(huì)花費(fèi)很多時(shí)間。當(dāng)主題狀態(tài)更新頻繁時(shí),通知時(shí)間的消耗可能會(huì)導(dǎo)致觀察者的更新時(shí)延。
3. 如果在觀察者和主題之間有循環(huán)依賴的話,主題的狀態(tài)變化會(huì)觸發(fā)它們之間進(jìn)行循環(huán)調(diào)用,可能導(dǎo)致系統(tǒng)崩潰。在使用觀察者模式是要特別注意這一點(diǎn)。
應(yīng)用場(chǎng)景舉例
1. ?普通粉絲關(guān)注微博大V,大V有動(dòng)態(tài)更新時(shí),粉絲的關(guān)注動(dòng)態(tài)會(huì)提示通知。這里粉絲就是觀察者,大V就是主題。粉絲可以選擇關(guān)注或取消關(guān)注,大V的更新也只會(huì)推送給關(guān)注了的粉絲。
2. 訂閱網(wǎng)紅直播間,當(dāng)主播開播,平臺(tái)及時(shí)推送開播消息到訂閱粉絲。
參考
1. 《Head First Design Patterns》
2. 《設(shè)計(jì)模式:可復(fù)用面向?qū)ο筌浖幕A(chǔ)》
推薦閱讀
站長 polarisxu
自己的原創(chuàng)文章
不限于 Go 技術(shù)
職場(chǎng)和創(chuàng)業(yè)經(jīng)驗(yàn)
Go語言中文網(wǎng)
每天為你
分享 Go 知識(shí)
Go愛好者值得關(guān)注

