更新時間:2018年12月07日11時50分 來源:傳智播客 瀏覽次數(shù):
---
typora-root-url: pics
---
# 設(shè)計模式之觀察者模式
## 概述
? 觀察者模式,有時又被稱為模型-視圖(View)模式,有的叫發(fā)布-訂閱模式,監(jiān)聽模式或從屬者模式,是軟件設(shè)計模式的一種。在此種模式中,一個目標物件管理所有相依于它的觀察者物件,并且在它本身的狀態(tài)改變時主動發(fā)出通知。一般通過呼叫各觀察者所提供的方法來實現(xiàn)。此種模式通常被用來實現(xiàn)事件處理系統(tǒng)。
## 重要的角色
1. 觀察者: 觀察目標對象的狀態(tài)、行為等的的主體。觀察將自己存放到被觀察對象中,被觀察對象將觀察者存放在一個容器(Vector)里。
2. 被觀察的對象:被觀察者所研究或關(guān)注的對象。當被觀察的對象發(fā)生了某種變化時,將循環(huán)遍歷容器中的所有觀察者,并將發(fā)生的變化通知予觀察者。
## 觀察者模式結(jié)構(gòu)圖
![observer_structure](/observer_structure.png)
### 說明
* Observable 是一個接口,定義了被觀察者主體所具備的方法,實現(xiàn)這個接口的主體可以擁有多個觀察者,當主體對象的狀態(tài)發(fā)生改變時,可以通知到所有的觀察者
* addObserver: 添加新的觀察者
* delObserver: 移除原有的觀察者
* notifyObservers: 當主體對象發(fā)生改變時,通知所有的觀察者
* Observer 也是一個接口,定義了觀察者所具備的方法,當主體發(fā)生變化時,通過調(diào)用update方法通知觀察者,你觀察的主體發(fā)生了變化
* ConcreteSubject 是Observable的具體實現(xiàn)類,被觀察的對象,也可稱為觀察的實 "物"
* ConcreteObserver 是Observer的實現(xiàn)類,實現(xiàn)這個接口的就是觀察者,可以實現(xiàn)對實 “物” 的觀察
## 舉例
? 我們都知道微信有個叫公眾號的服務(wù),只要公眾號發(fā)布新的消息,關(guān)注了這個公眾的微信用戶就能收最新的消息,如果用戶取消了該公眾號的關(guān)注,就不再收到由該公眾號發(fā)布出來的消息了。
## 分析
? 這里的微信公眾號可以看成是一個 被觀察對象,即觀察的對象,而關(guān)注了這個公眾號的所有用戶都是觀察者
? 下面我們可以通過代碼來簡單模擬一下
## 代碼實現(xiàn)
### 1. 創(chuàng)建普通java工程
? ![project](/project.png)
### 2. Observer
```java
package com.itheima.demo;
/**
* 觀察者接口
*/
public interface Observer {
/**
* 被觀察體發(fā)生變化時調(diào)用的方法,用來通知觀察者
* @param args
*/
void update(Object args);
/**
* 獲取觀察者名稱
* @return
*/
String getName();
}
```
### 3. Observable
```java
package com.itheima.demo;
/**
* 被觀察者接口
*
*/
public interface Observable {
/**
* 添加觀察者
* @param observer
*/
void addObserver(Observer observer);
/**
* 解除觀察者
* @param observer
*/
void delObserver(Observer observer);
/**
* 通知所有的觀察者
*/
void notifiyObservers(Object args);
}
```
### 4. WeChatOfficialAccount
```java
package com.itheima.demo;
import java.util.Vector;
/**
* 被觀察者,微信公眾號
*
*/
public class WeChatOfficialAccount implements Observable {
private String name;
/**
* 保存所有的觀察者
*/
private Vector observers;
public WeChatOfficialAccount(String name) {
this.name = name;
observers = new Vector();
}
@Override
public synchronized void addObserver(Observer observer) {
System.out.println(observer.getName() + " 關(guān)注了公眾號 " + this.name);
this.observers.add(observer);
}
@Override
public synchronized void delObserver(Observer observer) {
observers.remove(observer);
}
@Override
public void notifiyObservers(Object args) {
Observer[] arr = observers.toArray(new Observer[] {});
for(int i = arr.length - 1; i >= 0; i--) {
// 通知每個觀察者
arr[i].update(args);
}
}
/**
* 發(fā)布新的消息
* @param message
*/
public void postMessage(String message) {
System.out.println(name + "微信公眾號發(fā)布新的消息: " + message);
notifiyObservers(message);
}
}
```
### 5. WeChatUser
```java
package com.itheima.demo;
/**
* 微信用戶
*
*/
public class WeChatUser implements Observer {
private String username;
public WeChatUser(String username) {
this.username = username;
}
@Override
public void update(Object args) {
readMessage((String)args);
}
public void readMessage(String message) {
System.out.println(String.format("%s 讀取了信息-> %s", this.username,message));
}
@Override
public String getName() {
return this.username;
}
}
```
### 6. DemoMain 測試
```java
package com.itheima.demo;
public class DemoMain {
public static void main(String[] args) {
WeChatOfficialAccount wechat = new WeChatOfficialAccount("傳智播客");
WeChatUser wechatUser = new WeChatUser("張三");
wechat.addObserver(wechatUser);
wechatUser = new WeChatUser("李四");
wechat.addObserver(wechatUser);
wechatUser = new WeChatUser("王五");
wechat.addObserver(wechatUser);
wechat.postMessage("改變中國IT教育,我們正在行動...");
}
}
```
### 7. 運行結(jié)果
? ![result](/result.png)
## 總結(jié)
優(yōu)點:觀察者模式在被觀察者和觀察者之間建立一個抽象的耦合。被觀察者并不認識任何一個具體觀察者,當發(fā)生變化時,只需要調(diào)用它們共有的接口方法就可以了。
缺點:
* 如果一個被觀察者對象有很多的直接和間接的觀察者的話,將所有的觀察者都通知到會花費很多時間
* 如果在被觀察者之間有循環(huán)依賴的話,被觀察者會觸發(fā)它們之間進行循環(huán)調(diào)用,導致系統(tǒng)崩潰。
* 如果對觀察者的通知是通過另外的線程進行異步投遞的話,系統(tǒng)必須保證投遞是以自恰的方式進行的
* 雖然觀察者模式可以隨時使觀察者知道所觀察的對象發(fā)生了變化,但無法知道這個變化的過程