使用Observer模式改善現有軟件設計——構建簡便的通知

[quote]Observer又叫觀察者模式,它的意圖是當一個對象進行改變時,所有依賴於它的對象都可以得到通知。(觀察者模式定義了一種一對多地依賴模式,讓多個觀察者同時監聽某一個主題對象。這個主題對象在狀態發生變化時,會通知所有的觀察者對象,使它們能夠自動更新自己。這裏的主題對象就是指通知者,又叫做發佈者。觀察者又叫訂閱者。)[/quote]

聽起來這個模式似乎非常有用,它屬於對象行爲型模式,一般在一個系統中,把一個功能分割成由多個類共同完成,如果此時每個類之間需要進行同步的協作,怎麼辦?使用Observer解決問題。一般來說Observer適用於任何情況。

一般來說,我們可以在系統中讓每個相關聯的類再類與類中調用互相自己的引用來解決問題,達到通知的效果,但是這樣一來有個問題,類與類的關聯太大,偶合太大,而且過多引用容易產生碎對象。不過我想,現在有了DI框架,已經不是個問題了,但是如果不使用DI呢?問題還是有,我們種不會一直DI,當然,有些人願意這樣一直引入個SPRING IOC包,這個另當別論。如果在系統在設計時已經說明,對哪個類不可以修改,這個時候你要在類裏添加一個引用怎麼辦?(當然,這種情況不大可能發生。),如果你不停的引用,雖然你有IOC,但是有個問題你確無法很好解決,每次添加一個新類,你總是要修改原有類的方法,怎麼樣可以不改變代碼?很簡單,一個新類,繼承抽象基類,這就是Observer的用處所在,之後只需要把添加的新類註冊到一個列表裏既可。而本身這個類似於一個小容器的東西不僅可以提供註冊,它還有刪除和修改的列表的方法。

對於Observer模式,本身比較繁雜,但是有了JDK類庫內建的支持,使得使用Observer模式變的簡單,不需要進行類似於跟蹤觀察者,對通知的管理等等。但是JDK內建支持也有些問題,它必須要你使用繼承,一般直接繼承會有些問題,最重要的問題是內建的支持不夠靈活,所有爲了完成自己的業務規則,必須自己手動去實現整個Observer模式。

關於觀察者模式,在CSDN上有個很好的博文。地址:http://blog.csdn.net/wanghao72214/archive/2009/03/23/4017507.aspx
以下的代碼是從這個帖子裏複製來的。

//主題基類
import java.util.Vector;
public abstract class Subject {
//觀察者列表
private Vector<Observer> vectObserver = new Vector<Observer>();
//增加一個觀察者
public void attach(Observer observer){
vectObserver.add(observer);
}
//去除一個觀察者
public void detach(Observer observer){
vectObserver.remove(observer);
}
//通知觀察者更新
public void notifyObservers(){
for(int i=0; i<vectObserver.size(); i++){
Observer observer = vectObserver.get(i);
observer.update();
}
}
//獲取主題信息
public abstract String getSubject();
}
//銀行櫃檯類
public class Counter extends Subject{
//當前業務號
private String bizNo;
//櫃檯名稱
private String name;
//構造函數
public Counter(String name){
this.name = name;
}
//獲取當前業務號
public String getBizNo(){
return this.bizNo;
}
//設置當前業務號
public void setBizNo(String bizNo){
this.bizNo = bizNo;
}
//獲取主題信息
public String getSubject(){
return "請" + this.bizNo + "號到" + this.name + "號櫃檯辦理業務";
}
}
//管理部門類
public class Manager extends Subject{
//管理部門名稱
private String name;
//構造函數
public Manager(String name){
this.name = name;
}
//獲取主題信息
public String getSubject(){
return this.name + "發佈最新緊急公告";
}
}
//觀察者基類
public abstract class Observer {
protected String name;
protected Subject subject;
//構造函數
public Observer(String name,Subject subject){
this.name = name;
this.subject = subject;
}
//更新信息
public abstract void update();
}
//小顯示屏類
public class SmallScreen extends Observer{
//構造函數
public SmallScreen(String name,Subject subject){
super(name,subject);
}
//更新顯示屏
public void update(){
try{
System.out.println(this.name + ":" + subject.getSubject());
}
catch(Exception err){
}
}
}
//音箱類
public class Speaker extends Observer{
//構造函數
public Speaker(String name,Subject subject){
super(name,subject);
}
//更新音箱
public void update(){
try{
System.out.println(this.name + ":" + subject.getSubject());
}
catch(Exception err){
}
}
}
//業務系統類
public class BankBiz {
public static void main(String[] args) {
//銀行櫃檯
Counter counter = new Counter("1號櫃檯");
//1,2號小屏、3號音箱
SmallScreen smallScreen1 = new SmallScreen("1號小屏",counter);
SmallScreen smallScreen2 = new SmallScreen("2號小屏",counter);
Speaker speaker = new Speaker("3號音箱",counter);
//銀行櫃檯加入觀察者
counter.attach(smallScreen1);
counter.attach(smallScreen2);
counter.attach(speaker);
//9號辦理業務
counter.setBizNo("9");
//通知更新
counter.notifyObservers();

//管理部門
Manager manager = new Manager("風險控制部");
//1號小屏
smallScreen1 = new SmallScreen("1號小屏",manager);
//管理部門加入觀察者
manager.attach(smallScreen1);
//通知更新
manager.notifyObservers();
}
}

把主題與觀察者分別抽象出來,因爲主題與觀察者之間有可以公用的方法,之後讓每一個需要變成主題的類去繼承抽象主題基類,讓需要被觀察的類繼承觀察者抽象基類就可以了。

但是基於抽象基類的主題與觀察者還有些缺點,有的時候改變了註冊,刪除的業務規則時,我們還得修改代碼,有的時候爲了方便,我們可以直接把主題與觀察者抽像基類抽象成一個接口。之後在繼承接口類中去實現註冊,刪除,修改的方法。

光知道一個Observer的實現又有何用,要把Observer靈活運用到系統中才是王道。有的時候,我們的反映需要不是既時的,有的時候需要延時,有的時候甚至需要讓各子通知後的程序運行在不同的線層當中,或者有的時候這些對象需要跨越多個不同的生命週期,這時問題就開始複雜起來了。
發佈了30 篇原創文章 · 獲贊 1 · 訪問量 7850
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章