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

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

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營(yíng)銷解決方案
前端的設(shè)計(jì)模式系列-觀察者模式

大部分講設(shè)計(jì)模式的文章都是使用的 Java、C++ 這樣的以類為基礎(chǔ)的靜態(tài)類型語言,作為前端開發(fā)者,js 這門基于原型的動(dòng)態(tài)語言,函數(shù)成為了一等公民,在實(shí)現(xiàn)一些設(shè)計(jì)模式上稍顯不同,甚至簡(jiǎn)單到不像使用了設(shè)計(jì)模式,有時(shí)候也會(huì)產(chǎn)生些困惑。

成都創(chuàng)新互聯(lián)一直在為企業(yè)提供服務(wù),多年的磨煉,使我們?cè)趧?chuàng)意設(shè)計(jì),網(wǎng)絡(luò)營(yíng)銷推廣到技術(shù)研發(fā)擁有了開發(fā)經(jīng)驗(yàn)。我們擅長(zhǎng)傾聽企業(yè)需求,挖掘用戶對(duì)產(chǎn)品需求服務(wù)價(jià)值,為企業(yè)制作有用的創(chuàng)意設(shè)計(jì)體驗(yàn)。核心團(tuán)隊(duì)擁有超過十多年以上行業(yè)經(jīng)驗(yàn),涵蓋創(chuàng)意,策化,開發(fā)等專業(yè)領(lǐng)域,公司涉及領(lǐng)域有基礎(chǔ)互聯(lián)網(wǎng)服務(wù)成都棕樹電信機(jī)房、App定制開發(fā)、手機(jī)移動(dòng)建站、網(wǎng)頁設(shè)計(jì)、網(wǎng)絡(luò)整合營(yíng)銷。

下面按照「場(chǎng)景」-「設(shè)計(jì)模式定義」- 「代碼實(shí)現(xiàn)」-「總」的順序來總結(jié)一下,如有不當(dāng)之處,歡迎交流討論。

場(chǎng)景

假設(shè)我們?cè)陂_發(fā)一款外賣網(wǎng)站,進(jìn)入網(wǎng)站的時(shí)候,第一步需要去請(qǐng)求后端接口得到用戶的常用外賣地址。然后再去請(qǐng)求其他接口、渲染頁面。如果什么都不考慮可能會(huì)直接這樣寫:

// getAddress 異步請(qǐng)求
// 頁面里有三個(gè)模塊 A,B,C 需要拿到地址后再進(jìn)行下一步
// A、B、C 三個(gè)模塊都是不同人寫的,提供了不同的方法供我們調(diào)用

getAddress().then(res => {
const address = res.address;
A.update(address)
B.next(address)
C.change(address)
})

此時(shí)頁面里多了一個(gè)模塊 D ,同樣需要拿到地址后進(jìn)行下一步操作,我們只好去翻請(qǐng)求地址的代碼把 D 模塊的調(diào)用補(bǔ)上。

// getAddress 異步請(qǐng)求
// 頁面里有三個(gè)模塊 A,B,C 需要拿到地址后再進(jìn)行下一步
// A、B、C 三個(gè)模塊都是不同人寫的,提供了不同的方法供我們調(diào)用

getAddress().then(res => {
const address = res.address;
A.update(address)
B.next(address)
C.change(address)
D.init(address)
})

可以看到各個(gè)模塊和獲取地址模塊耦合嚴(yán)重,A、B、C 模塊有變化或者有新增模塊,都需要深入到獲取地址的代碼去修改,一不小心可能就改出問題了。

此時(shí)就需要觀察者模式了。

設(shè)計(jì)模式定義

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

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

很好理解的一個(gè)設(shè)計(jì)模式,有一個(gè) subject 對(duì)象,然后有很多 observers 觀察者對(duì)象,當(dāng) subject 對(duì)象有變化的時(shí)候去通知 observer 對(duì)象即可。

再看一下 UML 圖和時(shí)序圖:

每一個(gè)觀察者都實(shí)現(xiàn)了 update 方法,并且調(diào)用 Subject 對(duì)象的 attach方法訂閱變化。當(dāng) Subject 變化時(shí),調(diào)用 Observer 的 update 方法去通知觀察者。

先用 java 寫一個(gè)簡(jiǎn)單的例子:

公眾號(hào)文章可以看作是 Subject ,會(huì)不定期更新。然后每一個(gè)用戶都是一個(gè) Observer ,訂閱公眾號(hào),當(dāng)更新的時(shí)候就可以第一時(shí)間收到消息。

import java.util.ArrayList;

interface Observer {
public void update();
}
// 提取 Subject 的公共部分
abstract class Subject {
private ArrayList list = new ArrayList();
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();
}
}
}
// 具體的公眾號(hào),提供寫文章和得到文章
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;
}
@Override
public void update(){
String post = subject.getPost();
System.out.println("我收到了" + post + " 并且點(diǎn)了個(gè)贊");
}
}

// 小楊
class XiaoYang implements Observer {
private WindLiang subject;

XiaoYang(WindLiang sub) {
subject = sub;
}
@Override
public void update(){
String post = subject.getPost();
System.out.println("我收到了" + post + " 并且轉(zhuǎn)發(fā)了");
}
}

// 小剛
class XiaoGang implements Observer {
private WindLiang subject;

XiaoGang(WindLiang sub) {
subject = sub;
}
@Override
public void update(){
String post = subject.getPost();
System.out.println("我收到了" + post + " 并且收藏");
}
}


public class Main {
public static void main(String[] args) {
WindLiang windliang = new WindLiang(); // Subject
XiaoMing 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(); // 通知觀察者
}
}

輸出結(jié)果如下:

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

這兩種不同的方式前者叫做拉 (pull)模式,就是收到 Subject 的通知后,通過內(nèi)部的 Subject 對(duì)象調(diào)用相應(yīng)的方法去拿到需要的數(shù)據(jù)。

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

下邊用 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 + " 并且點(diǎn)了個(gè)贊");
}
}

const XiaoYang = {
update(post){
console.log("我收到了" + post + " 并且轉(zhuǎn)發(fā)了");
}
}

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 ,同時(shí)采取推模式,調(diào)用 update 的時(shí)候直接將數(shù)據(jù)傳給觀察者,看起來會(huì)簡(jiǎn)潔很多。

代碼實(shí)現(xiàn)

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

// 頁面里有三個(gè)模塊 A,B,C 需要拿到地址后再進(jìn)行下一步
// A、B、C 三個(gè)模塊都是不同人寫的,提供了不同的方法供我們調(diào)用
const observers = []
// 注冊(cè)觀察者
observers.push(A.update)
observers.push(B.next)
obervers.push(C.change)



// getAddress 異步請(qǐng)求
getAddress().then(res => {
const address = res.address;
observers.forEach(update => update(address))
})

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

當(dāng) getAddress 很復(fù)雜的時(shí)候,通過觀察者模式會(huì)使得未來的改動(dòng)變得清晰,不會(huì)影響到 getAddress 的邏輯。

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

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

Subject 仍需要自己維護(hù)一個(gè)觀察者列表,進(jìn)行push 和 update。

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

Subject 需要知道觀察者提供了什么方法以便未來的時(shí)候進(jìn)行回調(diào)。

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

但寫法上會(huì)引入一個(gè)中間平臺(tái),便于代碼更好的復(fù)用,使得 Subject 和觀察者進(jìn)行更加徹底的解耦,同時(shí)給了它一個(gè)新的名字「發(fā)布訂閱模式」。


名稱欄目:前端的設(shè)計(jì)模式系列-觀察者模式
鏈接分享:http://m.5511xx.com/article/ccccosh.html