面試官經常問的觀察者模式如何實現~

大家好,我是若川。持續組織了8個月源碼共讀活動,感興趣的可以?點此加我微信ruochuan12?參與,每周大家一起學習200行左右的源碼,共同進步。同時極力推薦訂閱我寫的《學習源碼整體架構系列》?包含20余篇源碼文章。歷史面試系列。另外:目前建有江西|湖南|湖北籍前端群,可加我微信進群。

本文作者有一個設計模式系列:https://pattern.windliang.wang/ 推薦大家收藏保存學習。

代碼也寫了幾年了,設計模式處于看了忘,忘了看的狀態,最近對設計模式有了點感覺,索性就再學習總結下吧。

大部分講設計模式的文章都是使用的 JavaC++ 這樣的以類為基礎的靜態類型語言,作為前端開發者,js 這門基于原型的動態語言,函數成為了一等公民,在實現一些設計模式上稍顯不同,甚至簡單到不像使用了設計模式,有時候也會產生些困惑。

下面按照「場景」-「設計模式定義」- 「代碼實現」-「總」的順序來總結一下,如有不當之處,歡迎交流討論。

場景

假設我們在開發一款外賣網站,進入網站的時候,第一步需要去請求后端接口得到用戶的常用外賣地址。然后再去請求其他接口、渲染頁面。如果什么都不考慮可能會直接這樣寫:

//?getAddress?異步請求
//?頁面里有三個模塊?A,B,C?需要拿到地址后再進行下一步
//?A、B、C?三個模塊都是不同人寫的,提供了不同的方法供我們調用getAddress().then(res?=>?{const?address?=?res.address;A.update(address)B.next(address)C.change(address)
})

此時頁面里多了一個模塊 D ,同樣需要拿到地址后進行下一步操作,我們只好去翻請求地址的代碼把 D 模塊的調用補上。

//?getAddress?異步請求
//?頁面里有三個模塊?A,B,C?需要拿到地址后再進行下一步
//?A、B、C?三個模塊都是不同人寫的,提供了不同的方法供我們調用getAddress().then(res?=>?{const?address?=?res.address;A.update(address)B.next(address)C.change(address)D.init(address)
})

可以看到各個模塊和獲取地址模塊耦合嚴重,ABC 模塊有變化或者有新增模塊,都需要深入到獲取地址的代碼去修改,一不小心可能就改出問題了。

此時就需要觀察者模式了。

設計模式定義

可以看下 維基百科的介紹:

The observer pattern is a software design pattern in which an object, named the subject, maintains a list of its dependents, called observers, and notifies them automatically of any state changes, usually by calling one of their methods.

很好理解的一個設計模式,有一個 subject 對象,然后有很多 observers 觀察者對象,當 subject 對象有變化的時候去通知 observer 對象即可。

再看一下 UML 圖和時序圖:

ca2dfc7cd0aa67d08fa0beda9913658c.png
image-20220127110751274

每一個觀察者都實現了 update 方法,并且調用 Subject 對象的 attach 方法訂閱變化。當 Subject 變化時,調用 Observerupdate 方法去通知觀察者。

先用 java 寫一個簡單的例子:

公眾號文章可以看作是 Subject ,會不定期更新。然后每一個用戶都是一個 Observer ,訂閱公眾號,當更新的時候就可以第一時間收到消息。

import?java.util.ArrayList;interface?Observer?{public?void?update();
}
//?提取?Subject?的公共部分
abstract?class?Subject?{private?ArrayList<Observer>?list?=?new?ArrayList<Observer>();public?void?attach(Observer?observer){list.add(observer);}public?void?detach(Observer?observer){list.remove(observer);}public?void?notifyObserver(){for(Observer?observer?:?list){observer.update();}}
}
//?具體的公眾號,提供寫文章和得到文章
class?WindLiang?extends?Subject?{private?String?post;public?void?writePost(String?p)?{post?=?p;}public?String?getPost()?{return?post;}
}//?小明
class?XiaoMing?implements?Observer?{private?WindLiang?subject;XiaoMing(WindLiang?sub)?{subject?=?sub;}@Overridepublic?void?update(){String?post?=?subject.getPost();System.out.println("我收到了"?+?post?+?"?并且點了個贊");}
}//?小楊
class?XiaoYang?implements?Observer?{private?WindLiang?subject;XiaoYang(WindLiang?sub)?{subject?=?sub;}@Overridepublic?void?update(){String?post?=?subject.getPost();System.out.println("我收到了"?+?post?+?"?并且轉發了");}
}//?小剛
class?XiaoGang?implements?Observer?{private?WindLiang?subject;XiaoGang(WindLiang?sub)?{subject?=?sub;}@Overridepublic?void?update(){String?post?=?subject.getPost();System.out.println("我收到了"?+?post?+?"?并且收藏");}
}public?class?Main?{public?static?void?main(String[]?args)?{WindLiang?windliang?=?new?WindLiang();?//?SubjectXiaoMing?xiaoMing?=?new?XiaoMing(windliang);XiaoYang?xiaoYang?=?new?XiaoYang(windliang);XiaoGang?xiaoGang?=?new?XiaoGang(windliang);//?添加觀察者windliang.attach(xiaoMing);windliang.attach(xiaoYang);windliang.attach(xiaoGang);windliang.writePost("新文章-觀察者模式,balabala");?//?更新文章windliang.notifyObserver();?//?通知觀察者}
}

輸出結果如下:

4a3fdd34d436c1325daf6eac38baf014.png

上邊的實現主要是為了符合最原始的定義,調用 update 的時候沒有傳參。如果觀察者需要的參數是一致的,其實這里也可以直接把更新后的數據傳過去,這樣觀察者就不需要向上邊一樣再去調用 subject.getPost() 手動拿更新后的數據了。

這兩種不同的方式前者叫做拉 (pull) 模式,就是收到 Subject 的通知后,通過內部的 Subject ?對象調用相應的方法去拿到需要的數據。

后者叫做推 (push) 模式,Subject 更新的時候就將數據推給觀察者,觀察者直接使用即可。

下邊用 js 改寫為推模式:

const?WindLiang?=?()?=>?{const?list?=?[];let?post?=?"還沒更新";return?{attach(update)?{list.push(update);},detach(update)?{let?findIndex?=?-1;for?(let?i?=?0;?i?<?list.length;?i++)?{if?(list[i]?===?update)?{findIndex?=?i;break;}}if?(findIndex?!==?-1)?{list.splice(findIndex,?1);}},notifyObserver()?{for?(let?i?=?0;?i?<?list.length;?i++)?{list[i](post);}},writePost(p)?{post?=?p;},};
};const?XiaoMing?=?{update(post){console.log("我收到了"?+?post?+?"?并且點了個贊");}
}const?XiaoYang?=?{update(post){console.log("我收到了"?+?post?+?"?并且轉發了");}
}const?XiaoGang?=?{update(post){console.log("我收到了"?+?post?+?"?并且收藏");}
}windliang?=?WindLiang();windliang.attach(XiaoMing.update)
windliang.attach(XiaoYang.update)
windliang.attach(XiaoGang.update)windliang.writePost("新文章-觀察者模式,balabala")
windliang.notifyObserver()

js 中,我們可以直接將 update 方法傳給 Subject ,同時采取推模式,調用 update 的時候直接將數據傳給觀察者,看起來會簡潔很多。

代碼實現

回到開頭的場景,我們可以利用觀察者模式將獲取地址后的一系列后續操作解耦出來。

//?頁面里有三個模塊?A,B,C?需要拿到地址后再進行下一步
//?A、B、C?三個模塊都是不同人寫的,提供了不同的方法供我們調用
const?observers?=?[]
//?注冊觀察者
observers.push(A.update)
observers.push(B.next)
obervers.push(C.change)//?getAddress?異步請求
getAddress().then(res?=>?{const?address?=?res.address;observers.forEach(update?=>?update(address))
})

通過觀察者模式我們將獲取地址后的操作解耦了出來,未來有新增模塊只需要注冊觀察者即可。

getAddress 很復雜的時候,通過觀察者模式會使得未來的改動變得清晰,不會影響到 getAddress 的邏輯。

必要的話也可以把 observers 抽離到一個新的文件作為一個新模塊,防止讓一個文件變得過于臃腫。

觀察者模式比較好理解,通過抽象出一個 Subject 和多個觀察者,減輕了它們之間的過度耦合。再說簡單點就是利用回調函數,異步完成后調用傳入的回調即可。但上邊寫的觀察者模式還是有一些缺點:

  • Subject 仍需要自己維護一個觀察者列表,進行 pushupdate

  • 如果有其他的模塊也需要使用觀察者模式,還需要模塊本身再維護一個新的觀察者列表,而不能復用之前的代碼。

  • Subject 需要知道觀察者提供了什么方法以便未來的時候進行回調。

下一篇文章會繼續改進上邊的寫法,觀察者模式的本質思想不變(某個對象變化,然后通知其他觀察者對象進行更新)。

但寫法上會引入一個中間平臺,便于代碼更好的復用,使得 Subject 和觀察者進行更加徹底的解耦,同時給了它一個新的名字「發布訂閱模式」。

更多設計模式推薦閱讀:

前端的設計模式系列-策略模式

前端的設計模式系列-代理模式

前端的設計模式系列-裝飾器模式

本文作者有一個設計模式系列:https://pattern.windliang.wang/ 推薦大家收藏保存學習。

0d7e11324cf5c09c6e27ab74c2524fbe.gif

·················?若川簡介?·················

你好,我是若川,畢業于江西高校。現在是一名前端開發“工程師”。寫有《學習源碼整體架構系列》20余篇,在知乎、掘金收獲超百萬閱讀。
從2014年起,每年都會寫一篇年度總結,已經堅持寫了8年,點擊查看年度總結。
同時,最近組織了源碼共讀活動,幫助4000+前端人學會看源碼。公眾號愿景:幫助5年內前端人走向前列。

a209edb0e6fed8cd9840e6f87f689ff1.png

掃碼加我微信 ruochuan02、拉你進源碼共讀

今日話題

目前建有江西|湖南|湖北?籍 前端群,想進群的可以加我微信 ruochuan12?進群。分享、收藏、點贊、在看我的文章就是對我最大的支持~

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

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

相關文章

旅行者 問題_門檻項目:沒有旅行者回到他的原籍城市。

旅行者 問題Sohini Mukherjee| MFA| Spring 2020Sohini Mukherjee | 外交部| 2020年Spring Artivive app to see the full Artivive應用程序可查看完整的#AR experience.#AR體驗。 Prompt:提示&#xff1a; As second semester, first year graduate students, you are at a …

產品經理懂技術=流氓會武術(zz)

最近七年&#xff0c;我都在做互聯網產品&#xff0c;其中前五年分別在創業公司和上市公司里&#xff0c;做別人的產品&#xff1b;近兩年在創業&#xff0c;做自己的產品。 我的體會是&#xff1a;產品經理需要懂技術&#xff0c;創業者尤其需要。但前提是你總覺得有股憋不住的…

技術人的七大必備特質

大家好&#xff0c;我是若川。持續組織了8個月源碼共讀活動&#xff0c;感興趣的可以 點此加我微信ruochuan12 參與&#xff0c;每周大家一起學習200行左右的源碼&#xff0c;共同進步。同時極力推薦訂閱我寫的《學習源碼整體架構系列》 包含20余篇源碼文章。歷史面試系列。另外…

figma下載_在Figma中進行原型制作的技巧和竅門

figma下載自定義過渡和微交互 (Custom transitions and micro-interactions) Yep, I know that there are a lot of useful built-in transition effects in Figma already, but here I want to talk about custom micro-interactions, complicated transitions and show you h…

布局收藏用

http://www.aa25.cn/layout/index.shtml轉載于:https://www.cnblogs.com/OceanChen/archive/2012/07/25/2608882.html

技術日新月異,發展迅速,如何不斷擴展視野

技術日新月異&#xff0c;發展迅速&#xff0c;作為一個與時俱進的互聯網人&#xff0c;需要不斷地學習擴寬視野。今天為大家推薦幾個技術領域中出類拔萃的公眾號&#xff0c;它們的每一篇推文都值得你點開&#xff01;1葉小釵技術管理 技術轉型 公司治理葉小釵&#xff0c;原騰…

不想當全棧的設計師不是_但我不想成為產品設計師

不想當全棧的設計師不是重點 (Top highlight)I’ve made a huge mistake, I thought to myself, as a realization washed over me in the middle of an interview for a product design role.我對自己想&#xff0c;我犯了一個巨大的錯誤&#xff0c;因為在接受產品設計職務的…

學習 WCF (6)--學習調用WCF服務的各種方法

來自&#xff1a;http://www.cnblogs.com/gaoweipeng/archive/2009/07/26/1528263.html 根據不同的情況&#xff0c;我們可以用不同的方法調用WCF服務&#xff0c;本文簡單總結了一下調用WCF的一些方法(代理類&#xff0c;Ajax...)&#xff0c;分享給大家。開發工具調用WCF 這中…

[科普文] Vue3 到底更新了什么?

Vue3 已經發布一段時間了&#xff0c;這個版本從底層實現到上層 API 設計都發生了非常大的變化&#xff0c;但具體改變了些什么呢&#xff1f;一起簡單盤點下&#xff1a;一、Composition API使用傳統的option配置方法寫組件的時候問題&#xff0c;隨著業務復雜度越來越高&…

ipados_如何設計具有最新iPadOS 14功能的出色iPad應用

ipadosWe all know that iPad Pro already has a seriously powerful computing power and that it’s possible to create meaningful stuff with Apple Pen.我們都知道iPad Pro已經具有強大的計算能力&#xff0c;并且可以使用Apple Pen創建有意義的東西。 But do we really…

分組顯示的ListView分頁加載數據

參考&#xff1a; http://www.cnblogs.com/qianxudetianxia/archive/2011/06/07/2074326.html 分組的ListView的拖拽 http://www.cnblogs.com/qianxudetianxia/archive/2011/06/13/2079253.html ListView分頁加載數據 http://blog.csdn.net/cjjky/article/details/6898871轉載于…

67行JS代碼實現隊列取代數組,面試官刮目相看

大家好&#xff0c;我是若川。持續組織了8個月源碼共讀活動&#xff0c;感興趣的可以 點此加我微信ruochuan12 參與&#xff0c;每周大家一起學習200行左右的源碼&#xff0c;共同進步。同時極力推薦訂閱我寫的《學習源碼整體架構系列》 包含20余篇源碼文章。歷史面試系列。另外…

ux和ui_我怎么知道UI / UX是否適合我?

ux和ui重點 (Top highlight)I’m super excited to be writing this as it’s the first official issue of Visual Q’s! If you don’t already know, this will be a monthly advice column for designers. If you join the newsletter, you’ll receive this before it goe…

HTML4和HTML5的區別[轉]

HTML5是最新的HTML標準&#xff0c;或遲或早&#xff0c;所有的web程序員都會發現需要使用到這個最新的標準&#xff0c;而且&#xff0c;很多人都會感覺到&#xff0c;重新開發一個HTML5的網站&#xff0c;要比把一個網站從HTML4遷移到HTML5上容易的多&#xff0c;這是因為這兩…

vs2017字體最佳選擇_如何為下一個項目選擇最佳字體? 一個簡單的游戲

vs2017字體最佳選擇“If I have the right font, half my design battle is already won!”“如果我使用正確的字體&#xff0c;那么我的設計大戰已經贏了一半&#xff01;” In my first UX Design job, my AVP( Satish if you’re reading this, this one’s for you. ) onc…

淺談初中級前端學習方法~

大家好&#xff0c;我是若川。 常有小伙伴問我如何學習前端開發。今天就簡單談下學習方法&#xff0c;方法可能主要適用于初中級前端。回想我們高中學習&#xff0c;是不是都是"以課本為主&#xff0c;其他資料為輔"。而且課堂上記筆記&#xff0c;然后通過大量練習&…

HDU-水餃基情 二維樹狀數組

該題就是簡單的二維樹狀數組&#xff0c;保留一份棋盤的最新狀態即可&#xff0c;樹狀數組里面就只保留在原有基礎上增加或者減少的某一種餃子的數量。 代碼如下&#xff1a; #include <cstring> #include <cstdlib> #include <cstdio> using namespace std;…

ui設計中的版式設計_設計中的版式-第3部分

ui設計中的版式設計and how not to suck at it以及如何不吸吮它 This is the 3rd and last part of the series. Here we take all our learnings from Part 1(Click to read) & Part 2(Click to read) and put to good use. Lets begin!這是本系列的第三部分也是最后一部…

聽說你還在用開發者工具手動上傳小程序,快來試試 miniprogram-ci 提效摸魚

大家好&#xff0c;我是若川。持續組織了8個月源碼共讀活動&#xff0c;感興趣的可以 點此加我微信ruochuan12 參與&#xff0c;每周大家一起學習200行左右的源碼&#xff0c;共同進步。同時極力推薦訂閱我寫的《學習源碼整體架構系列》 包含20余篇源碼文章。歷史面試系列。另外…

ucla ai_UCLA的可持續性:用戶體驗案例研究

ucla aiRole: UX Researcher / UX Designer / Critical-thinker角色&#xff1a; UX研究人員/ UX設計人員/批判性思維者 Scope: 4 weeks, March — March 2020范圍&#xff1a; 4周&#xff0c;2020年3月至2020年3月 What I Did: UX Research, Speculative Design, Product D…