<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          設(shè)計(jì)模式之觀察者模式

          共 5309字,需瀏覽 11分鐘

           ·

          2020-09-05 04:28


          觀察者模式定義了對(duì)象之間的一對(duì)多依賴,當(dāng)一個(gè)對(duì)象狀態(tài)改變時(shí),它的所有依賴者都會(huì)收到通知并自動(dòng)更新。

          觀察者模式中有兩個(gè)關(guān)鍵對(duì)象,分別是Subject(主題)和Observer(觀察者)。它們之間是一種發(fā)布訂閱關(guān)系,一(主題)對(duì)多(觀察者)。

          主題:管理主題數(shù)據(jù),在數(shù)據(jù)更新時(shí)通知觀察者(發(fā)布)。

          觀察者:訂閱或注冊(cè)主題,當(dāng)主題數(shù)據(jù)發(fā)生更改,接受主題的發(fā)布消息。


          應(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}


          為襯衫(shirt)對(duì)象定義了三種狀態(tài)status:TimeIsUp代表可以開搶了;IsEnd代表搶購結(jié)束;NotAtTime代表搶購還未開始。初始化襯衫對(duì)象時(shí),默認(rèn)狀態(tài)為NotAtTimeshirtcustomers中存儲(chǔ)的是訂閱了主題(襯衫)的觀察者們(想搶購襯衫的顧客們)。


          • 創(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é)


          松耦合


          在觀察者模式中,主題和觀察者之間是松耦合的。

          對(duì)于主題而言,它只知道觀察者實(shí)現(xiàn)了Observer接口,主題不需要知道觀察者的具體類是誰、做了什么或其他任何細(xì)節(jié)。因?yàn)橹黝}唯一依賴的東西(即上文代碼中shirtcustomers)是一個(gè)實(shí)現(xiàn)Observer接口的對(duì)象列表,所以我們可以隨時(shí)增加觀察者。同樣的,也可以在任何時(shí)候刪除某些觀察者。

          在新類型的觀察者出現(xiàn)時(shí),主題的代碼不需要修改。假如我們有個(gè)新的具體類需要當(dāng)觀察者,我們不需要為了兼容新類型而修改主題的代碼,所有要做的就是在新的類里實(shí)現(xiàn)此觀察者接口,然后注冊(cè)為觀察者即可。


          改變主題或觀察者其中一方,并不會(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ǔ)






          推薦閱讀



          學(xué)習(xí)交流 Go 語言,掃碼回復(fù)「進(jìn)群」即可


          站長 polarisxu

          自己的原創(chuàng)文章

          不限于 Go 技術(shù)

          職場(chǎng)和創(chuàng)業(yè)經(jīng)驗(yàn)


          Go語言中文網(wǎng)

          每天為你

          分享 Go 知識(shí)

          Go愛好者值得關(guān)注


          瀏覽 63
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  特级黄色片 | 新中文字幕亚洲 | 青娱乐在线视频自拍好爽好舒服啊 | 欧美精品福利 | 久久九九热re6这里有精品 |