寫在前面
- 記錄學(xué)習(xí)設(shè)計(jì)模式的筆記
- 提高對設(shè)計(jì)模式的靈活運(yùn)用
學(xué)習(xí)地址
https://www.bilibili.com/video/BV1G4411c7N4
https://www.bilibili.com/video/BV1Np4y1z7BU
參考文章
http://c.biancheng.net/view/1317.html
項(xiàng)目源碼
https://gitee.com/zhuang-kang/DesignPattern
21,觀察者模式
21.1 觀察者模式的定義和特點(diǎn)
觀察者(Observer)模式的定義:指多個(gè)對象間存在一對多的依賴關(guān)系,當(dāng)一個(gè)對象的狀態(tài)發(fā)生改變時(shí),所有依賴于它的對象都得到通知并被自動(dòng)更新。這種模式有時(shí)又稱作發(fā)布-訂閱模式、模型-視圖模式,它是對象行為型模式。
觀察者模式是一種對象行為型模式,其主要優(yōu)點(diǎn)如下。
- 降低了目標(biāo)與觀察者之間的耦合關(guān)系,兩者之間是抽象耦合關(guān)系。符合依賴倒置原則。
- 目標(biāo)與觀察者之間建立了一套觸發(fā)機(jī)制。
它的主要缺點(diǎn)如下。
- 目標(biāo)與觀察者之間的依賴關(guān)系并沒有完全解除,而且有可能出現(xiàn)循環(huán)引用。
- 當(dāng)觀察者對象很多時(shí),通知的發(fā)布會(huì)花費(fèi)很多時(shí)間,影響程序的效率。
21.2 觀察者模式的結(jié)構(gòu)與實(shí)現(xiàn)
實(shí)現(xiàn)觀察者模式時(shí)要注意具體目標(biāo)對象和具體觀察者對象之間不能直接調(diào)用,否則將使兩者之間緊密耦合起來,這違反了面向?qū)ο蟮脑O(shè)計(jì)原則
21.2.1 觀察者模式的結(jié)構(gòu)
- 抽象主題(Subject)角色:也叫抽象目標(biāo)類,它提供了一個(gè)用于保存觀察者對象的聚集類和增加、刪除觀察者對象的方法,以及通知所有觀察者的抽象方法。
- 具體主題(Concrete Subject)角色:也叫具體目標(biāo)類,它實(shí)現(xiàn)抽象目標(biāo)中的通知方法,當(dāng)具體主題的內(nèi)部狀態(tài)發(fā)生改變時(shí),通知所有注冊過的觀察者對象。
- 抽象觀察者(Observer)角色:它是一個(gè)抽象類或接口,它包含了一個(gè)更新自己的抽象方法,當(dāng)接到具體主題的更改通知時(shí)被調(diào)用。
- 具體觀察者(Concrete Observer)角色:實(shí)現(xiàn)抽象觀察者中定義的抽象方法,以便在得到目標(biāo)的更改通知時(shí)更新自身的狀態(tài)。
21.2.1 代碼實(shí)現(xiàn)
在使用微信公眾號時(shí),大家都會(huì)有這樣的體驗(yàn),當(dāng)你關(guān)注的公眾號中有新內(nèi)容更新的話,它就會(huì)推送給關(guān)注公眾號的微信用戶端。我們使用觀察者模式來模擬這樣的場景,微信用戶就是觀察者,微信公眾號是被觀察者,有多個(gè)的微信用戶關(guān)注了程序猿這個(gè)公眾號
關(guān)系類圖
Observer
package com.zhuang.observer;
/**
* @Classname Observer
* @Description 抽象觀察者
* @Date 2021/3/28 14:14
* @Created by dell
*/
public interface Observer {
//更新的方法
void update(String messages);
}
WexinUser
package com.zhuang.observer;
/**
* @Classname WexinUser
* @Description 具體觀察者類 實(shí)現(xiàn)更新的方法
* @Date 2021/3/28 14:14
* @Created by dell
*/
public class WexinUser implements Observer {
//用戶名
private String name;
public WexinUser(String name) {
this.name = name;
}
public WexinUser() {
}
@Override
public void update(String messages) {
System.out.println(name + "-->" + messages);
}
}
Subject
package com.zhuang.observer;
/**
* @Classname Subject
* @Description 抽象主題類
* @Date 2021/3/28 14:15
* @Created by dell
*/
public interface Subject {
//增加訂閱者
public void attach(Observer observer);
//刪除訂閱者
public void remove(Observer observer);
//通知訂閱者更新消息
public void notify(String messages);
}
SubscriptionSubject
package com.zhuang.observer;
import java.util.ArrayList;
import java.util.List;
/**
* @Classname SubscriptionSubject
* @Description 具體主題(具體被觀察者)
* @Date 2021/3/28 14:15
* @Created by dell
*/
public class SubscriptionSubject implements Subject {
//存儲訂閱公眾號的微信用戶
private List<Observer> weixinUserList = new ArrayList<Observer>();
@Override
public void attach(Observer observer) {
weixinUserList.add(observer);
}
@Override
public void remove(Observer observer) {
weixinUserList.remove(observer);
}
@Override
public void notify(String messages) {
for (Observer observer : weixinUserList) {
observer.update(messages);
}
}
}
Client
package com.zhuang.observer;
/**
* @Classname Client
* @Description 觀察者模式 測試類
* @Date 2021/3/28 14:16
* @Created by dell
*/
public class Client {
public static void main(String[] args) {
SubscriptionSubject subject = new SubscriptionSubject();
//創(chuàng)建微信用戶
WexinUser user1 = new WexinUser("張三");
WexinUser user2 = new WexinUser("李四");
WexinUser user3 = new WexinUser("王五");
//訂閱公眾號
subject.attach(user1);
subject.attach(user2);
subject.attach(user3);
//通過訂閱用戶
subject.notify("您關(guān)注的公眾號更新啦~~~");
}
}
20.3 觀察者模式應(yīng)用場景
- 對象間存在一對多關(guān)系,一個(gè)對象的狀態(tài)發(fā)生改變會(huì)影響其他對象。
- 當(dāng)一個(gè)抽象模型有兩個(gè)方面,其中一個(gè)方面依賴于另一方面時(shí),可將這二者封裝在獨(dú)立的對象中以使它們可以各自獨(dú)立地改變和復(fù)用。
- 實(shí)現(xiàn)類似廣播機(jī)制的功能,不需要知道具體收聽者,只需分發(fā)廣播,系統(tǒng)中感興趣的對象會(huì)自動(dòng)接收該廣播。
- 多層級嵌套使用,形成一種鏈?zhǔn)接|發(fā)機(jī)制,使得事件具備跨域(跨越兩種觀察者類型)通知。
20.4 JDK源碼解析
在 Java 中,通過 java.util.Observable 類和 java.util.Observer 接口定義了觀察者模式,只要實(shí)現(xiàn)它們的子類就可以編寫觀察者模式實(shí)例。
1,Observable類
Observable 類是抽象目標(biāo)類(被觀察者),它有一個(gè) Vector 集合成員變量,用于保存所有要通知的觀察者對象,下面來介紹它最重要的 3 個(gè)方法。
void addObserver(Observer o) 方法:用于將新的觀察者對象添加到集合中。
void notifyObservers(Object arg) 方法:調(diào)用集合中的所有觀察者對象的 update方法,通知它們數(shù)據(jù)發(fā)生改變。通常越晚加入集合的觀察者越先得到通知。
void setChange() 方法:用來設(shè)置一個(gè) boolean 類型的內(nèi)部標(biāo)志,注明目標(biāo)對象發(fā)生了變化。當(dāng)它為true時(shí),notifyObservers() 才會(huì)通知觀察者。
2,Observer 接口
Observer 接口是抽象觀察者,它監(jiān)視目標(biāo)對象的變化,當(dāng)目標(biāo)對象發(fā)生變化時(shí),觀察者得到通知,并調(diào)用 update 方法,進(jìn)行相應(yīng)的工作。
【例】警察抓小偷
警察抓小偷也可以使用觀察者模式來實(shí)現(xiàn),警察是觀察者,小偷是被觀察者。代碼如下:
小偷是一個(gè)被觀察者,所以需要繼承Observable類
package com.zhuang.observer.observable_example;
import java.util.Observable;
/**
* @Classname Thief
* @Description 小偷類 繼承Observable接口
* @Date 2021/3/28 14:36
* @Created by dell
*/
public class Thief extends Observable {
private String name;
public Thief(String name) {
this.name = name;
}
public Thief() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void steal() {
System.out.println("小偷:我偷東西,有沒有人來抓我?。?!");
super.setChanged();//默認(rèn)為true
super.notifyObservers();
}
}
警察是一個(gè)觀察者,所以需要讓其實(shí)現(xiàn)Observer接口
package com.zhuang.observer.observable_example;
import java.util.Observable;
import java.util.Observer;
/**
* @Classname Policemen
* @Description 警察類 實(shí)現(xiàn)Observe類 實(shí)現(xiàn)update方法
* @Date 2021/3/28 14:36
* @Created by dell
*/
public class Policemen implements Observer {
private String name;
public Policemen(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Policemen() {
}
@Override
public void update(Observable o, Object arg) {
System.out.println("警察:" + ((Thief) o).getName() + "你被我抓到了哈哈哈哈!??!");
}
}
客戶端代碼
package com.zhuang.observer.observable_example;
/**
* @Classname Client
* @Description 測試類
* @Date 2021/3/28 14:36
* @Created by dell
*/
public class Client {
public static void main(String[] args) {
//小偷對象
Thief thief = new Thief("法外狂徒張三");
//警察對象
Policemen policemen = new Policemen("小莊警察");
//警察盯著小偷
thief.addObserver(policemen);
//小偷行竊
thief.steal();
}
}
寫在最后
- 如果我的文章對你有用,請給我點(diǎn)個(gè)??,感謝你??!
- 有問題,歡迎在評論區(qū)指出!??