認識觀察者模式
《Head First 設計模式》非常通俗易懂,生動活潑的向我們展示了,各種設計模式,同樣觀察者模式,也使用《Head First 設計模式》中的小栗子,來解釋什麼是觀察者模式。
先看一下報社和訂戶的栗子:
報社的業務就是出版報紙。
向某家報社訂閱報紙,只要他們有新報紙出版,就會給你送來。只要你是他們的訂戶,你就會一直收到新報紙。
當你不想再看報紙的時候,取消訂閱,他們就不會再送新報紙來。
只要報社還在運營,就會一直有人(或單位)向他們訂閱報紙或取消訂閱報紙。
出版者 + 訂閱者 = 觀察者模式
我們把名稱改一下,報社即出版者改爲主題Subject,訂戶即訂閱者改爲觀察者Observer,
那如何定義觀察者模式呢?當然你也可以使用報社的例子來比對適合觀察者模式的場景。
觀察者模式定義了對象之間的一對多依賴,這樣一來,當一個對象改變狀態時,它的所有依賴者都會收到通知並自動更新。觀察者模式屬於行爲型模式。
類圖:
- Subject(interface):這是主題接口,對象使用此接口註冊爲觀察者,或者把自己從觀察者中刪除。
- ConcreteSubject(class):一個具體主題總是實現主題接口,除了註冊和撤銷方法之外,具體主題還實現了notifyObservers()方法,此方法用於在狀態改變時更新所有當前觀察者。
- Observer(interface):所有潛在的觀察者必須實現觀察者接口,這個接口只有update()一個方法, 當主題狀態改變時它被調用。
- ConcreteObserver(class):具體的觀察者可以是實現Observer接口的任意類。觀察者必須註冊具體主題,以便接收更新。
觀察者模式的好處(鬆耦合)
當兩個對象之間鬆耦合,它們依然可以交互,但是不太清楚彼此的細節。
觀察者模式提供了一種對象設計,讓主題和觀察者之間鬆耦合。
爲什麼呢?
關於觀察者的一切,主題只知道觀察者實現了某個接口,也就是Observer接口。主
題不需要知道觀察者的具體類是誰、做了些什麼或其他任何細節。任何時候我們都可增加新的觀察者。因爲主題唯一依賴的東西是一個實現Observer接口的對象列表,所以我們可以隨時增加觀察者。
事實上,在運行時我們可以用新的觀察者取代現有的觀察者,主題不會受到任何影響。同樣的,也可以在任何時候刪除某些觀察者。有新類型的觀察者出現時,主題的代碼不需要修改。假如我們有個新的具體類需要當觀察者,我們不需要爲了兼容新類型而修改主題的代碼,所有要做的就是在新的類裏實現此觀察者接口,然後註冊爲觀察者即可。主題不在乎別的,它只會發送通知給所有實現了觀察者接口的對象。我們可以獨立地複用主題或觀察者。如果我們在其他地方需要使用主題或觀察者,可以輕易地複用,因爲二者並非緊耦合。
改變主題或觀察者其中一方,並不會影響另一方。因爲兩者是鬆耦合的,所以只要他
們之間的接口仍被遵守,我們就可以自由地改變他們。
設計原則
爲了交互對象之間的鬆耦合設計而努力。
鬆耦合的設計之所以能讓我們建立有彈性的OO系統,能夠應對變化,
是因爲對象之間的互相依賴降到了最低。
針對接口編程,不針對實現。
多用組合少用繼承
具體實現
接下來要實現一個,生活中的觀察者模式。
當然觀察者可能只有一個,也可能只有一個。
比如說你第一次去你的女朋友家裏去,七大姑八大姨就是觀察者Observer,你就是被觀察者也就是Subject。這裏只以父母舉個栗子。
類結構:
Subject 被觀察者主題接口
//被觀察者主題接口
public interface Subject {
//註冊成爲觀察者
void registObserver(Observer o);
//反註冊移除觀察者
void unRegistObserver(Observer o);
//通知所有觀察者變化
void notifyAllObservers();
}
Observer 觀察者接口
//觀察者接口
public interface Observer {
void update(int score);//接收分數
}
Evaluate 評價接口
//評價接口
public interface Evaluate {
void evaluate(int score);//根據接受的分數對你進行評價
}
You (主題)被觀察者的實現類
//(主題)被觀察者的實現類
public class You implements Subject {
private ArrayList<Observer> observers;
private int score;
You() {
observers = new ArrayList<Observer>();
}
public void registObserver(Observer o) {
if (o != null) {
observers.add(o);
}
}
public void unRegistObserver(Observer o) {
int i = observers.indexOf(o);
if (i >= 0) {
observers.remove(i);
}
}
public void yourBehavior(int whatBehaviorScore) {
this.score = whatBehaviorScore;
notifyAllObservers();
}
public void notifyAllObservers() {
for (Observer o : observers) {
o.update(score);
}
}
}
YourGirlFriendFather觀察者的實現類 你女朋友的爸爸
//觀察者的實現類 你女朋友的爸爸
public class YourGirlFriendFather implements Observer ,Evaluate{
private Subject you;
private int score;
YourGirlFriendFather(Subject you) {
this.you = you;
you.registObserver(this);
}
public void update(int score) {
this.score = score;
evaluate(score);
}
public void evaluate(int score) {
int fatherScore = score + 20;
switch (fatherScore) {
case 20:
System.out.println("father:還不錯有待考察");
break;
case 80:
System.out.println("father:問問他家長啥時候見個面,這小夥子不錯");
break;
case 100:
System.out.println("father:跟他家長要個號碼,直接訂婚");
break;
case 120:
System.out.println("father:你倆先去,領結婚證");
break;
}
}
}
YourGirlFriendMother 觀察者的實現類 你女朋友的媽媽
//觀察者的實現類 你女朋友的媽媽
public class YourGirlFriendMother implements Observer,Evaluate {
private Subject you;
private int score;
YourGirlFriendMother(Subject you) {
this.you = you;
you.registObserver(this);
}
public void update(int score) {
this.score = score;
evaluate(score);
}
public void evaluate(int score) {
int motherScore = score + 25;
switch (motherScore) {
case 25:
System.out.println("mother:還行,比較傻,說話還不甜");
break;
case 85:
System.out.println("mother:挺溫柔,對你很不錯,會說話");
break;
case 105:
System.out.println("mother:居家好男人,掙錢多,又高又帥");
break;
case 125:
System.out.println("mother:有房有車,有掙錢,脾氣又好,又高又帥,說話甜");
break;
}
}
}
TestObserver 你女朋友的父母觀察你的言行舉止對你進行評價(觀察者模式測試)
//觀察者模式測試
public class TestObserver {
public static void main(String[] args) {
You you = new You();
YourGirlFriendFather father = new YourGirlFriendFather(you);
YourGirlFriendMother mother = new YourGirlFriendMother(you);
you.yourBehavior(0);
you.yourBehavior(60);
you.yourBehavior(80);
you.yourBehavior(100);
}
}
運行打印:
father:還不錯有待考察
mother:還行,比較傻,說話還不甜
father:問問他家長啥時候見個面,這小夥子不錯
mother:挺溫柔,對你很不錯,會說話
father:跟他家長要個號碼,直接訂婚
mother:居家好男人,掙錢多,又高又帥
father:你倆先去,領結婚證
mother:有房有車,有掙錢,脾氣又好,又高又帥,說話甜
觀察者模式在實際應用中的缺點和優點總結
優點
- 觀察者和被觀察者之間是抽象耦合,應對業務變化能力強,可擴展性強;
- 觀察者模式支持廣播通訊。被觀察者會向所有的登記過的觀察者發出通知。
同時觀察者模式也有一些缺點
- 觀察者模式有時會有開發效率和運行效率的問題,程序中包括一個被觀察者和多個觀察者時,調試和開發會相對變得複雜;
- 觀察者如果數量很多,所有的觀察者都通知到會花費一定時間;
- 觀察者模式中被觀察者通知觀察者時默認是順序執行,所以當一個觀察者執行緩慢或者卡頓就會影響整體的執行效率,如果有這種情況最好採用異步執行的方式去處理;
- 如果在被觀察者之間有循環依賴的話,被觀察者會觸發它們之間進行循環調用,導致系統崩潰,在使用觀察者模式時要特別注意這一點。
參考
《Head First 設計模式》