觀察者模式【Observer Pattern 】
觀察者模式以秦國時期李斯監視韓非子爲例。
先看下類圖:
被觀察者:
public interface IHanFeiZi {
//韓非子也是人,也要吃早飯的
public void haveBreakfast();
//韓非之也是人,是人就要娛樂活動
public void haveFun();
}
被觀察者的實現類:
public class HanFeiZi implements IHanFeiZi{
//韓非子是否在吃飯,作爲監控的判斷標準
private boolean isHaveBreakfast = false;
//韓非子是否在娛樂
private boolean isHaveFun = false;
//韓非子要吃飯了
public void haveBreakfast(){
System.out.println("韓非子:開始吃飯了...");
this.isHaveBreakfast =true;
}
//韓非子開始娛樂了,古代人沒啥娛樂,你能想到的就那麼多
public void haveFun(){
System.out.println("韓非子:開始娛樂了...");
this.isHaveFun = true;
}
//以下是bean的基本方法,getter/setter,不多說
public boolean isHaveBreakfast() {
return isHaveBreakfast;
}
public void setHaveBreakfast(boolean isHaveBreakfast) {
this.isHaveBreakfast = isHaveBreakfast;
}
public boolean isHaveFun() {
return isHaveFun;
}
public void setHaveFun(boolean isHaveFun) {
this.isHaveFun = isHaveFun;
}
}
觀察者的接口:
public interface ILiSi {
//一發現別人有動靜,自己也要行動起來
public void update(String context);
}
觀察者的實現類:
public class LiSi implements ILiSi{
//首先李斯是個觀察者,一旦韓非子有活動,他就知道,他就要向老闆彙報
public void update(String str){
System.out.println("李斯:觀察到韓非子活動,開始向老闆彙報了...");
this.reportToQiShiHuang(str);
System.out.println("李斯:彙報完畢,秦老闆賞給他兩個蘿蔔吃吃...\n");
}
//彙報給秦始皇
private void reportToQiShiHuang(String reportContext){
System.out.println("李斯:報告,秦老闆!韓非子有活動了--->"+reportContext);
}
}
全部搞好上面了,就要來個監測程序了。
class Watch extends Thread{
private HanFeiZi hanFeiZi;
private LiSi liSi;
private String type;
//通過構造函數傳遞參數,我要監控的是誰,誰來監控,要監控什麼
public Watch(HanFeiZi _hanFeiZi,LiSi _liSi,String _type){
this.hanFeiZi =_hanFeiZi;
this.liSi = _liSi;
this.type = _type;
}
@Override
public void run(){
while(true){
if(this.type.equals("breakfast")){ //監控是否在吃早餐
//如果發現韓非子在吃飯,就通知李斯
if(this.hanFeiZi.isHaveBreakfast()){
this.liSi.update("韓非子在吃飯");
//重置狀態,繼續監控
this.hanFeiZi.setHaveBreakfast(false);
}
}else{//監控是否在娛樂
if(this.hanFeiZi.isHaveFun()){
this.liSi.update("韓非子在娛樂");
this.hanFeiZi.setHaveFun(false);
}
}
}
}
}
這樣子是可以完成對韓非子的監測,但仔細想想,就不行了,這樣子while(true)開啓死循環來進行監測,服務器肯定是受不了的,而且監測的對象只有一個,你以爲你是秦始皇。。。還是大出血的修改一下好:韓非子是大人物,肯定會有很多間諜監視的,於是觀察者應該提煉出來,觀察者要觀察的也不只是人家的吃法娛樂吧,故也提煉出來:
觀察者:
public interface Observable {
//增加一個觀察者
public void addObserver(Observer observer);
//刪除一個觀察者,——我不想讓你看了
public void deleteObserver(Observer observer);
//既然要觀察,我發生改變了他也應該用所動作——通知觀察者
public void notifyObservers(String context);
}
被觀察者實現上面的接口:
public class HanFeiZi implements Observable{
//定義個變長數組,存放所有的觀察者
private ArrayList<Observer> observerList = new ArrayList<Observer>();
//增加觀察者
public void addObserver(Observer observer){
this.observerList.add(observer);
}
//刪除觀察者
public void deleteObserver(Observer observer){
this.observerList.remove(observer);
}
//通知所有的觀察者
public void notifyObservers(String context){
for(Observer observer:observerList){
observer.update(context);
}
}
//韓非子要吃飯了
public void haveBreakfast(){
System.out.println("韓非子:開始吃飯了...");
//通知所有的觀察者
this.notifyObservers("韓非子在吃飯");
}
//韓非子開始娛樂了,古代人沒啥娛樂,你能想到的就那麼多
public void haveFun(){
System.out.println("韓非子:開始娛樂了...");
this.notifyObservers("韓非子在娛樂");
}
}
再看觀察者:
public interface Observer {
//一發現別人有動靜,自己也要行動起來
public void update(String context);
}
然後觀察者來實現這個接口即可,有多少個觀察者都行。
在主類中:
//三個觀察者產生出來
Observer liSi = new LiSi();
Observer wangSi = new WangSi();
Observer liuSi = new LiuSi();
//定義出韓非子
HanFeiZi hanFeiZi = new HanFeiZi();
//我們後人根據歷史,描述這個場景,有三個人在觀察韓非子
hanFeiZi.addObserver(liSi);
hanFeiZi.addObserver(wangSi);
hanFeiZi.addObserver(liuSi);
//然後這裏我們看看韓非子在幹什麼
hanFeiZi.haveBreakfast();
這樣就完成了監視的過程。當然JDK 中提供了:
java.util.Observable 實現類和 java.util.Observer 接口,於是乎上面寫的那個例子中的
Observable 接口可以改換成 java.util.Observale 實現類了。
下面看一下通用類圖:
總結:
使用觀察者模式要考慮的問題:
(1)廣播鏈的問題。如果你做過數據庫的觸發器,你就應該知道有一個觸發器鏈的問題,比如表 A 上寫了一個觸發器,內容是一個字段更新後更新表 B 的一條數據,而表 B 上也有個觸發器,要更新表 C,表 C 也有觸發器…,完蛋了,這個數據庫基本上就毀掉了!我們的觀察者模式也是一樣的問題,一個觀察者可以有雙重身份,即使觀察者,也是被觀察者,這沒什麼問題呀,但是鏈一旦建立,這個邏輯就比較複雜,可維護性非常差,根據經驗建議,在一個觀察者模式中最多出現一個對象既是觀察者也是被觀察者,也就是說消息最多轉發一次(傳遞兩次) ,這還是比較好控制的;
(2)異步處理問題。被觀察者發生動作了,觀察者要做出迴應,如果觀察者比較多,而且處理時間比較長怎麼辦?那就用異步。
舉一個應用場景:
比如你到 ATM 機器上取錢,多次輸錯密碼,卡就會被 ATM吞掉,吞卡動作發生的時候,會觸發哪些事件呢?第一攝像頭連續快拍,第二,通知監控系統,吞卡發生;
第三,初始化 ATM 機屏幕,返回最初狀態,你不能因爲就吞了一張卡,整個 ATM 都不能用了吧,一般前兩
個動作都是通過觀察者模式來完成的。