設計模式九: 觀察者模式(Observer Pattern)
阿新 • • 發佈:2018-12-09
簡介
觀察者屬於行為型模式的一種, 又叫釋出-訂閱模式. 如果一個物件的狀態發生改變,依賴他的物件都將發生變化, 那麼這種情況就適合使用觀察者模式.
它包含兩個術語,主題(Subject),觀察者(Observer), 主題管理一個觀察者的列表, 並在狀態發生變化時通知到他們.
實現層面上, 主題定義了一個觀察者列表並可以管理這個列表(將觀察者註冊進去和撤銷註冊的行為), 同時定義了通知到所有觀察者的行為.
Java類庫有預設的觀察者實現類Observer. 使用觀察者模式的還有事件監聽, RxJava等.
意圖
定義物件之間的一對多依賴,當一個物件狀態改變時,它的所有依賴都會收到通知並且自動更新狀態。
類圖
實現
一. 定義抽象觀察者
/**
* 觀察者抽象角色
*/
public interface MyObserver {
void update(Subject subject);
}
二. 定義抽象主題
/** * 主題抽象角色 */ public abstract class Subject { private ArrayList<MyObserver> list = new ArrayList<MyObserver>(); public Subject register(MyObserver observer){ if(!list.contains(observer)){ list.add(observer); } return this; } public Subject unRegister(MyObserver observer){ if(list.contains(observer)){ list.remove(observer); } return this; } public final void notifyObservers(){ for (MyObserver item:list) { item.update(this); } } /** * 具體狀態的變更 */ abstract void change(); }
三. 定義具體主題
/**
* 具體主題角色
*/
@Data
public class ConcreteSubject extends Subject {
private String name;
public void change() {
this.setName("王多魚");
super.notifyObservers();
}
}
四. 定義觀察者的實現, 分別定義兩個不同實現
public class UncleObserver implements MyObserver { public void update(Subject subject) { ConcreteSubject concreteSubject = (ConcreteSubject)subject; System.out.println("UncleObserver 知道了: "+concreteSubject.getName()); } } public class HentaiObserver implements MyObserver { public void update(Subject subject) { ConcreteSubject concreteSubject = (ConcreteSubject)subject; System.out.println("HentaiObserver 知道了: "+concreteSubject.getName()); } }
五. 呼叫, 首先註冊觀察者,然後呼叫主題的變更方法
// 例項化主題後為其註冊了兩個觀察者的例項, 並最終呼叫主題的變更方法觸發通知行為
public static void main(String[] args) {
new ConcreteSubject()
.register(new UncleObserver())
.register(new HentaiObserver())
.change();
}
總結
關鍵點: 定義通知到觀察者的方法要注意, 如果有耗時嚴重的會阻塞其他觀察者得到通知, 可以非同步多執行緒實現; 避免迴圈引用;
觀察者分推模式和拉模式, 推模式是指將主題的變更資訊傳送給觀察者, 觀察者可以用, 也可以不用. 拉模式則是將主題的引用傳送給觀察者, 觀察者通過引用根據自己需要獲取所需資訊. 示例中使用的就是這種模式.
程式碼修改為推模式
// 觀察者的方法接收具體資訊
void update(String name);
// 主題的通知方法做相應修改
public final void notifyObservers(){
for (MyObserver item:list) {
item.update("王多魚");
}
}