相信大家都有看過《喜洋洋與灰太狼》,說的是灰太狼和羊族的“鬥爭”,而每次的結果都是灰太狼一飛沖天,伴隨著一句“我還會回來的......”。為灰太狼感到悲哀,抓不到羊,在家也被老婆平底鍋虐待。灰太狼為什麼會這麼背?
很簡單,灰太狼本身就有“暴露行蹤”的屬性,羊咩咩就能知曉灰太狼要幹嘛,不背才怪呢。
為了幫助灰太狼擺脫被老婆平底鍋抽的悲劇,發起了“解救灰太狼”的行動,必須要知道觀察者模式。
一、觀察者模式
定義
觀察者模式又叫做釋出-訂閱模式,定義了物件間一對多的依賴關係,使得當物件狀態發生變化時,所有依賴它的物件都會收到通知並且自動更新自己。
特點
1)被觀察者需要持有一個或者多個觀察者物件。
2)系統中一個模組的變化,某些模組也會跟隨著變化。
UML
從上面的UML可以看出來,觀察者模式設計到的角色有如下四個:
- 抽象被觀察者角色:定義了動態增加、刪除以及通知觀察者物件的方法,職責就是管理和通知觀察者。持有觀察者物件的集合。
- 具體被觀察者角色:一般繼承抽象被觀察者,實現自己本身的業務邏輯,當狀態發生改變時發起通知。
- 抽象觀察者角色:提供一個介面,定義了觀察者收到通知時更新自己的方法。
- 具體觀察者角色:實現抽象觀察者介面,處理不同具體觀察者的不同業務邏輯。
二、實戰
灰太狼具有被觀察者屬性,喜洋洋這些羊咩咩一直都在觀察者灰太狼,所以羊咩咩們是觀察者。OK,角色確定了,看看具體是怎麼實現的...
抽象被觀察者程式碼如下:
public abstract class Subject {
/**
* 觀察者物件的集合
*/
private List<Observer> observerList = new ArrayList<>();
/**
* 登記觀察者
*
* @param observer
*/
public void attach(Observer observer) {
observerList.add(observer);
System.out.println("增加了觀察者:" + observer.getName());
}
/**
* 刪除觀察者
*
* @param observer
*/
public void dettach(Observer observer) {
observerList.remove(observer);
System.out.println("刪除了觀察者:" + observer.getName());
}
/**
* 通知所有觀察者
*/
public void notifyObserver() {
for (Observer observer : observerList) {
observer.update("灰太狼要搞事情了");
}
}
}
複製程式碼
灰太狼是具體被觀察者,繼承抽象被觀察者,程式碼如下:
public class Wolf extends Subject {
public void invade(){
System.out.println("灰太狼:我要搞事情了");
// 通知所有觀察者
notifyObserver();
}
}
複製程式碼
抽象觀察者程式碼如下:
public interface Observer {
String getName();
/**
* 通知更新方法
*
* @param msg
*/
public void update(String msg);
}
複製程式碼
喜羊羊是具體觀察者,實現抽象觀察者,程式碼如下:
public class PleasantSheep implements Observer{
@Override
public String getName() {
return "喜羊羊";
}
/**
* 具體業務邏輯
*/
@Override
public void update(String msg) {
System.out.println("喜羊羊收到通知:" + msg);
}
}
複製程式碼
接下來看客戶端如何把觀察者模式跑起來,程式碼如下:
public class Client {
public static void main(String[] args) {
// 灰太狼--被觀察者
Wolf wolf = new Wolf();
// 喜羊羊--觀察者
Observer pleasantSheep = new PleasantSheep();
// 登記觀察者
wolf.attach(pleasantSheep);
// 灰太狼入侵
wolf.invade();
}
}
複製程式碼
執行客戶端程式碼,結果如下:
增加了觀察者:喜羊羊
灰太狼:我要搞事情了
喜羊羊收到通知:灰太狼要搞事情了
看到了吧,灰太狼這不是自找虐嗎!搞事情還要發通知,活該被平底鍋拍飛。灰太狼不止通知了喜羊羊,還通知了懶羊羊。
懶羊羊也是具體觀察者,程式碼如下:
public class LazySheep implements Observer {
@Override
public String getName() {
return "懶羊羊";
}
@Override
public void update(String msg) {
System.out.println("懶羊羊收到通知:" + msg);
}
}
複製程式碼
客戶端程式碼如下:
public class Client {
public static void main(String[] args) {
// 灰太狼--被觀察者
Wolf wolf = new Wolf();
// 喜羊羊--觀察者
Observer pleasantSheep = new PleasantSheep();
// 登記觀察者
wolf.attach(pleasantSheep);
// 懶羊羊--觀察者
Observer lazySheep = new LazySheep();
// 登記觀察者
wolf.attach(lazySheep);
// 灰太狼入侵
wolf.invade();
}
}
複製程式碼
上面客戶端程式碼建立了一個懶羊羊觀察者,新增了觀察者集合中,這樣懶羊羊也會受到通知,執行結果如下:
增加了觀察者:喜羊羊
增加了觀察者:懶羊羊
灰太狼:我要搞事情了
喜羊羊收到通知:灰太狼要搞事情了
懶羊羊收到通知:灰太狼要搞事情了
那如何幫助灰太狼擺脫這個命運呢,把觀察者從集合中移除就OK了,程式碼如下:
public class Client {
public static void main(String[] args) {
// 灰太狼--被觀察者
Wolf wolf = new Wolf();
// 喜羊羊--觀察者
Observer pleasantSheep = new PleasantSheep();
// 登記觀察者
wolf.attach(pleasantSheep);
// 懶羊羊--觀察者
Observer lazySheep = new LazySheep();
// 登記觀察者
wolf.attach(lazySheep);
// 灰太狼入侵
wolf.invade();
// 刪除觀察者
wolf.dettach(pleasantSheep);
wolf.invade();
}
}
複製程式碼
再次執行客戶端,結果如下:
增加了觀察者:喜羊羊
增加了觀察者:懶羊羊
灰太狼:我要搞事情了
喜羊羊收到通知:灰太狼要搞事情了
懶羊羊收到通知:灰太狼要搞事情了
刪除了觀察者:喜羊羊
灰太狼:我要搞事情了
懶羊羊收到通知:灰太狼要搞事情了
可以看到,把喜羊羊從觀察者集合中移除了,它就不會再收到通知。
三、觀察者模式的優缺點
優點
1)觀察者和被觀察者之間抽象耦合。觀察者模式容易擴充套件,被觀察者只持有觀察者集合,並不需要知道具體觀察者內部的實現。
2)物件之間的保持高度的協作。當被觀察者發生變化時,所有被觀察者都會通知到,然後做出相應的動作。
缺點
1)如果觀察者太多,被觀察者通知觀察者消耗的時間很多,影響系統的效能。
2)當觀察者集合中的某一觀察者錯誤時就會導致系統卡殼,因此一般會採用非同步方式。
四、比較
跟代理模式對比:觀察者模式和代理模式主要區別在它們功能不一樣,觀察者模式強調的是被觀察者反饋結果,而代理模式是同根負責做同樣的事情。
總結
在Java中已經提供了Observable類以及一個Observer介面,也就是說Java已經實現了觀察者模式的定義,可看出觀察者模式在程式系統中的使用率是很高的,不單是Java,Android中也經常看到觀察者模式的運用,比如OnClickListener,Rxjava等。下一篇會補上屬於建立型模式的原型模式,下回分解,再見。
設計模式Java原始碼GitHub下載:github.com/jetLee92/De…