一.基本概念:當對象間存在一對多關係時,則使用觀察者模式(Observer Pattern)。比如,當一個對象被修改時,則會自動通知它的依賴對象。觀察者模式屬於行爲型模式。
二。介紹:
意圖:定義對象間的一種一對多的依賴關係,當一個對象的狀態發生改變時,所有依賴於它的對象都得到通知並被自動更新。
主要解決:一個對象狀態改變給其他對象通知的問題,而且要考慮到易用和低耦合,保證高度的協作。
何時使用:一個對象(目標對象)的狀態發生改變,所有的依賴對象(觀察者對象)都將得到通知,進行廣播通知。
如何解決:使用面向對象技術,可以將這種依賴關係弱化。
關鍵代碼:在抽象類裏有一個 ArrayList 存放觀察者們。
應用實例: 1、拍賣的時候,拍賣師觀察最高標價,然後通知給其他競價者競價。 2、西遊記裏面悟空請求菩薩降服紅孩兒,菩薩灑了一地水招來一個老烏龜,這個烏龜就是觀察者,他觀察菩薩灑水這個動作。
優點: 1、觀察者和被觀察者是抽象耦合的。 2、建立一套觸發機制。
缺點: 1、如果一個被觀察者對象有很多的直接和間接的觀察者的話,將所有的觀察者都通知到會花費很多時間。 2、如果在觀察者和觀察目標之間有循環依賴的話,觀察目標會觸發它們之間進行循環調用,可能導致系統崩潰。 3、觀察者模式沒有相應的機制讓觀察者知道所觀察的目標對象是怎麼發生變化的,而僅僅只是知道觀察目標發生了變化。
使用場景:
- 一個抽象模型有兩個方面,其中一個方面依賴於另一個方面。將這些方面封裝在獨立的對象中使它們可以各自獨立地改變和複用。
- 一個對象的改變將導致其他一個或多個對象也發生改變,而不知道具體有多少對象將發生改變,可以降低對象之間的耦合度。
- 一個對象必須通知其他對象,而並不知道這些對象是誰。
- 需要在系統中創建一個觸發鏈,A對象的行爲將影響B對象,B對象的行爲將影響C對象……,可以使用觀察者模式創建一種鏈式觸發機制。
注意事項: 1、JAVA 中已經有了對觀察者模式的支持類。 2、避免循環引用。 3、如果順序執行,某一觀察者錯誤會導致系統卡殼,一般採用異步方式。
三.下面以明星粉絲爲例(參考網上的),
上面這位明星在新浪微博上發了一條動態,說他會唱、跳rap等等。然後他的粉絲就都知道了。從這個例子中我們可以看到,這裏包含了兩種人,第一種是明星,第二個是粉絲。轉化爲設計模式中的語言就是主題和觀察者。
我們的明星的微博就相當於與一個主題,粉絲就是觀察者,隨時觀察明星的動態。不過明星有權利讓你關注,也有權利把你拉黑。現在我們從類圖的角度來看一下:
從上面我們可以看到,這裏麪包含了兩大類(主題和觀察者)一共四個角色:
(1)Subject:抽象主題,他把所有觀察者對象保存在一個集合裏,可以有任意數量的觀察者,抽象主題提供一個接口,可以增加和刪除觀察者對象。意思就是明星把所有的粉絲都保存在一個賬號裏面,粉絲數量不限,可以新增粉絲也可以拉黑粉絲。
(2)ConcreteSubject:具體主題,該角色將有關狀態存入具體觀察者對象,在具體主題的內部狀態發生改變時,給所有註冊過的觀察者發送通知。意思是我們的明星一有動態,就會把消息給粉絲。
(3)Observer:抽象觀察者,是觀察者者的抽象類,它定義了一個更新接口,使得在得到主題更改通知時更新自己。這就是我們所有粉絲的抽象。
(4)ConcrereObserver:具體觀察者,實現抽象觀察者定義的更新接口,以便在得到主題更改通知時更新自身的狀態。具體每一個粉絲。
觀察者模式還是比較簡單的,就是一個粉絲關注明星的實例。現在代碼來實現一下。
四、代碼實現觀察者模式
這個例子我們還是那上面的例子來解釋說明。
第一步:定義抽象觀察者(Observer):抽象的粉絲
public interface FanObserver {
public void update(String message);
}
第二步:具體的觀察者(concreteObserver):具體的粉絲
public class ConcreteFan implements FanObserver{
private String fanName;
public ConcreteFan(String fanName) {
this.fanName=fanName;
}
@Override
public void update(String message) {
System.out.println(fanName+":"+message);
}
}
第三步:定義抽象主題(Subject):抽象明星
public interface MingSubject {
public void addFan(FanObserver fan);
public void delFan(FanObserver fan);
public void notify(String message );
}
第四步:具體主題(ConcreteSubject):具體的明星
import java.util.ArrayList;
import java.util.List;
public class ConcreteMing implements MingSubject{
private List<FanObserver> list = new ArrayList<FanObserver>();
@Override
public void addFan(FanObserver fan) {
list.add(fan);
// TODO Auto-generated method stub
}
@Override
public void delFan(FanObserver fan) {
list.remove(fan);
// TODO Auto-generated method stub
}
@Override
public void notify(String message) {
// TODO Auto-generated method stub
for(FanObserver fan : list) {
fan.update(message);
}
}
}
第五步:測試
public class Test {
public static void main(String[] args) {
ConcreteMing ming = new ConcreteMing();
ConcreteFan fan = new ConcreteFan("zhangsan");
ConcreteFan fan1 = new ConcreteFan("lisi");
ConcreteFan fan2 = new ConcreteFan("wanger");
ming.addFan(fan);
ming.addFan(fan1);
ming.addFan(fan2);
ming.notify("唱,跳,rap");
}
}
結果:
五。如果我們需要通知的不只是粉絲這一類人,還有朋友,親人等。
第一:加入朋友類
public class ConcreteFri implements FanObserver{
private String fanName;
public ConcreteFri(String fanName) {
this.fanName=fanName;
}
@Override
public void update(String message) {
System.out.println(fanName+":朋友"+message);
}
}
第二:修改測試類
public class Test {
public static void main(String[] args) {
ConcreteMing ming = new ConcreteMing();
//粉絲
ConcreteFan fan = new ConcreteFan("zhangsan");
ConcreteFan fan1 = new ConcreteFan("lisi");
ConcreteFan fan2 = new ConcreteFan("wanger");
//朋友
ConcreteFri fri = new ConcreteFri("cxkfri");
ming.addFan(fan);
ming.addFan(fan1);
ming.addFan(fan2);
ming.addFan(fri);
ming.notify("唱,跳,rap");
}
}
結果: