概念
Observer模式定義對象間的一對多的依賴關係,當一個對象(被觀察者)的狀態發生改變時, 所有依賴於它的對象(觀察者)都得到通知並被自動更新。JDK裡提供的observer設計模式的實現由java.util.Observable類和 java.util.Observer介面組成。從名字上可以清楚的看出兩者在Observer 設計模式中分別扮演的角色:Observer是觀察者角色,Observable是被觀察目標(subject)角色。
相關類介紹
1.Obervable類
此類表示模型視圖範例中的 observable 對象,繼承它的類表示應用程式想要觀察的對象。一個 observable 對象可以有一個或多個觀察者。觀察者是實現Observer介面的任意對象。一個 observable 執行個體改變後,調用 Observable 的 notifyObservers 方法的應用程式會通過調用觀察者的 update 方法來通知觀察者該執行個體發生了改變。
方法摘要 |
void |
addObserver(Observer o) 如果觀察者與集合中已有的觀察者不同,則向對象的觀察者集中添加此觀察者。未指定向多個觀察者發送通知的順序。 |
protected void |
clearChanged() 指示對象不再改變,或者它已對其所有的觀察者通知了最近的改變,所以 hasChanged 方法將返回 false。notifyObservers 方法自動調用此方法。 |
int |
countObservers() 返回 Observable 對象的觀察者數目。 |
void |
deleteObserver(Observer o) 從對象的觀察者集合中刪除某個觀察者。向此方法傳遞 null無效。 |
void |
deleteObservers() 清除觀察者列表,使此對象不再有任何觀察者。 |
boolean |
hasChanged() 測試對象是否改變。若且唯若在此對象上最近調用了 setChanged 方法時才返回 true;否則返回 false。 |
void |
notifyObservers() 如果 hasChanged 方法指示對象已改變,則通知其所有觀察者,並調用 clearChanged 方法來指示此對象不再改變。 每個觀察者都有其 update 方法,其調用參數有兩個:observable 對象和 null。換句話說,此方法等效於: notifyObservers(null) |
void |
notifyObservers(Object arg) 如果 hasChanged 方法指示對象已改變,則通知其所有觀察者,並調用 clearChanged 方法來指示此對象不再改變。 每個觀察者都有其 update 方法,其調用參數有兩個:observable 對象和 arg 參數。 arg 可以是任意對象 |
protected void |
setChanged() 標記此 Observable 對象為已改變的對象;現在 hasChanged 方法將返回 true。 |
關於發送通知的順序
Observable 類中所提供的預設實現將按照其註冊的重要性順序來通知 Observers,但是子類可能改變此順序,從而使用非固定順序在單獨的線程上發送通知,或者也可能保證其子類遵從其所選擇的順序。
注意:此通知機制與線程無關,並且與 Object 類的 wait 和 notify 機制完全獨立。
新建立一個 observable 對象時,其觀察者集是空的。若且唯若 equals 方法為兩個觀察者返回 true 時,才認為它們是相同的。
2.Oberver介面
這是個介面類,這個介面只有一個為實現的抽象方法update。實現該介面的對象成為觀察者,該對象要實現update方法。註冊了該對象(觀察者)的對象(觀察者)執行個體條用notifiyObservers方法後,觀察者會自動執行update方法。
方法摘要 |
void |
update(Observable o, Object arg) 只要改變了 observable 對象就調用此方法。 o - observable 對象。 arg - notifyObservers 方法的參數。 |
執行個體
該執行個體類比了燒水的過程,涉及三個對象,Heater(熱水器),Display(顯示器),Alarm(通報器).
類比過程:為了便於運行,水的初始化溫度為90,沸點為95,顯示器依據熱水器顯示溫度,顯示器顯示溫度為95時,通報器開始警示。明顯可以看出Heater是subject ,Display 是它的 Obsrver,同時Display亦是subject,因為它要被通報器觀察,所以Alarm是Display的Observer.
Heater.java類
public class Heater extends Observable {
private int temperature;
public int getTemperature() {
return temperature;
}
public void setTemperature(int temperature) {
this.temperature = temperature;
}
public void boilWater() {
for (int i = 90; i < 110; i++) {
temperature = i;
this.setChanged();
this.notifyObservers();
}
}
}
Display.java類
public class Display extends Observable implements Observer {
private String status = "未開";
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
@Override
public void update(Observable o, Object arg) {
this.displayTemperature(((Heater) o).getTemperature());
}
private void displayTemperature(int temperature) {
if (temperature > 100) {
this.setStatus("沸騰");
this.setChanged();
this.notifyObservers(temperature);
}
System.out.println("狀態: " + status + " 現在溫度: " + temperature);
}
}
Alarm.java類
public class Alarm implements Observer {
@Override
public void update(Observable arg0, Object arg1) {
this.makeAlarm((Integer) arg1);
}
private void makeAlarm(int temperature) {
System.out.println("嘀嘀嘀。。。水已經燒開了");
// System.out.println("現在水溫是: " + temperature);
}
}
TestMain.java測試入口類
public class TestMain {
public static void main(String[] args) {
Heater heater = new Heater();
Display display = new Display();
Alarm alarm = new Alarm();
heater.addObserver(display);
display.addObserver(alarm);
heater.boilWater();
}
}
優點
- 支援松耦合和減少依賴性。用戶端不再依賴於觀察器,因為通過使用主體和 Observer 介面對用戶端進行了隔離。許多架構具有此優點,在這些架構中的應用程式組件可以註冊為當(低級)架構事件發生時得到通知。結果,架構將調用應用程式組件,但不會依賴於它。
- 觀察器數目可變。觀察器可以在運行時附加和分離,因為主體對於觀察器數目沒有任何假定。此功能在這樣的情況下是很有用的:觀察器數在設計時是未知的。例如,如果使用者在應用程式中開啟的每個視窗都需要一個觀察器。
缺點
- 效能降低。在許多實現中,觀察器的 update() 方法可能與主體在同一線程中執行。如果觀察器列表很長,則執行 Notify() 方法可能需要很長時間。抽取對象依賴性並不意味著添加觀察器對應用程式沒有任何影響。
- 記憶體流失。在 Observer 中使用的回調機制(當對象註冊為以後調用時)會產生一個常見的錯誤,從而導致記憶體流失,甚至是在託管的 C# 代碼中。假定觀察器超出作用範圍,但忘記取消對主體的訂閱,那麼主體仍然保留對觀察器的引用。此引用防止垃圾收集在主體對象也被破壞之前重新分配與觀察器關聯的記憶體。如果觀察器的生存期比主體的生存期短得多(通常是這種情況),則會導致嚴重的記憶體流失。
- 隱藏的依賴項。觀察器的使用將顯式依賴性(通過方法調用)轉變為隱式依賴性(通過觀察器)。如果在整個應用程式中廣泛地使用觀察器,則開發人員幾乎不可能通過查看原始碼來瞭解所發生的事情。這樣,就使得瞭解代碼更改的含意非常困難。此問題隨傳播層級急劇增大(例如,充當 Subject 的觀察器)。因此,應該僅在少數定義良好的互動(如 Model-View-Controller 模式中模型和視圖之間的互動)中使用觀察器。最好不要在域對象之間使用觀察器。
- 測試 / 調試困難。儘管松耦合是一項重大的體繫結構功能,但是它可以使開發更困難。將兩個對象去耦的情況越多,在查看原始碼或類的關係圖時瞭解它們之間的依賴性就越難因此,僅當可以安全地忽略兩個對象之間的關聯時才應該將它們松耦合(例如,如果觀察器沒有副作用)。
參考文章:
http://baike.baidu.com/view/6547055.htm
http://msdn.microsoft.com/zh-cn/library/ms978753.aspx