觀察者模式:
未用模式實現:
1 //前臺類 2 public class Secretary { 3 private List<StockObserver> observers = new ArrayList<StockObserver>(); 4 private String action; 5 //添加同事 6 public void attach(StockObserver observer){ 7 observers.add(observer); 8 } 9 //待老板來時,前臺通過電話通知所有同事 10 public void notifyObserver(){ 11 for (StockObserver o : observers) { 12 o.update(); 13 } 14 } 15 public String getAction() { 16 return action; 17 } 18 public void setAction(String action) { 19 this.action = action; 20 } 21 22 }
?
//同事類
1 public class StockObserver { 2 //姓名 3 private String name; 4 //前臺 5 private Secretary secretary; 6 public StockObserver(String name, Secretary secretary) { 7 super(); 8 this.name = name; 9 this.secretary = secretary; 10 } 11 public void update() { 12 System.out.println(secretary.getAction()+ 13 ","+name+"關閉股票行情,繼續工作!"); 14 } 15 }
?
主方法1 public class Main { 2 public static void main(String[] args) { 3 //前臺小姐通知者 4 Secretary tongzhizhe = new Secretary(); 5 //兩位公司同事 6 StockObserver tongshi1 = new StockObserver("魏關姹", tongzhizhe); 7 StockObserver tongshi2 = new StockObserver("易管查", tongzhizhe); 8 //前臺幾下兩位同事 9 tongzhizhe.attach(tongshi1); 10 tongzhizhe.attach(tongshi2); 11 //老板來了.前臺通知同事 12 tongzhizhe.setAction("老板來了"); 13 tongzhizhe.notifyObserver(); 14 } 15 }
?
? 代碼雖然將需求的基本功能實現了,但是同事類需要前臺小姐的狀態,前臺小姐需要增加同事。這是典型的雙向偶爾。這段代碼及違反了開放封閉原則,又違反了依賴倒轉原則。解耦實現:
//同事抽象類
public abstract class Observer {
private String name;
public abstract void update();
}
同事實現類//同事類
public class StockObserver extends Observer{
//姓名
private String name;
private Subject subject;
public StockObserver(String name,Subject subject) {
super();
this.name = name;
this.subject = subject;
}
@Override
public void update() {
System.out.println(subject.getAction()+","+name+"關閉股票行情,繼續工作!");
}
}
//同事類
public class StockObserver2 extends Observer{
//姓名
private String name;
private Subject subject;
public StockObserver2(String name,Subject subject) {
super();
this.name = name;
this.subject = subject;
}
@Override
public void update() {
System.out.println(subject.getAction()+","+name+"關閉NBA直播,繼續工作!");
}
}
通知者抽象類
//通知者抽象類
public abstract class Subject {
String action;
private List<Observer> observers = new ArrayList<Observer>();
//添加同事
public abstract void attach(Observer observer);
//待老板來時,前臺通過電話通知所有同事
public abstract void notifyObserver();
public String getAction() {
return action;
}
public void setAction(String action) {
this.action = action;
}
}
通知者實現類://前臺類
public class Secretary extends Subject{
private List<Observer> observers = new ArrayList<Observer>();
private String action;
//添加同事
public void attach(StockObserver observer){
observers.add(observer);
}
//待老板來時,前臺通過電話通知所有同事
public void notifyObserver(){
for (Observer o : observers) {
o.update();
}
}
@Override
public void attach(Observer observer) {
observers.add(observer);
}
public String getAction() {
return action;
}
public void setAction(String action) {
this.action = action;
}
}
主程序:
public class Main {
public static void main(String[] args) {
//前臺小姐通知者
Secretary tongzhizhe = new Secretary();
//兩位公司同事
Observer tongshi1 = new StockObserver("魏關姹",tongzhizhe);
Observer tongshi2 = new StockObserver2("易管查",tongzhizhe);
//前臺幾下兩位同事
tongzhizhe.attach(tongshi1);
tongzhizhe.attach(tongshi2);
//老板來了.前臺通知同事
tongzhizhe.setAction("老板來了");
tongzhizhe.notifyObserver();
}
}
觀察者模式定義:
觀察者模式UML類圖:
觀察者模式特點:
推模型和拉模型
在觀察者模式中,又分為推模型和拉模型兩種方式。
● 推模型
主題對象向觀察者推送主題的詳細信息,不管觀察者是否需要,推送的信息通常是主題對象的全部或部分數據。
● 拉模型
主題對象在通知觀察者的時候,只傳遞少量信息。如果觀察者需要更具體的信息,由觀察者主動到主題對象中獲取,
相當于是觀察者從主題對象中拉數據。一般這種模型的實現中,會把主題對象自身通過update()方法傳遞給觀察者,這樣在觀察者需要獲取數據的時候,
就可以通過這個引用來獲取了。
根據上面的描述,發現前面的例子就是典型的推模型,下面給出一個拉模型的實例。
拉模型的抽象觀察者類
拉模型通常都是把主題對象當做參數傳遞。
public interface Observer {/*** 更新接口* @param subject 傳入主題對象,方面獲取相應的主題對象的狀態*/public void update(Subject subject); }
拉模型的具體觀察者類
public class ConcreteObserver implements Observer {//觀察者的狀態private String observerState;@Overridepublic void update(Subject subject) {/*** 更新觀察者的狀態,使其與目標的狀態保持一致*/observerState = ((ConcreteSubject)subject).getState();System.out.println("觀察者狀態為:"+observerState);}}
拉模型的抽象主題類
拉模型的抽象主題類主要的改變是nodifyObservers()方法。在循環通知觀察者的時候,也就是循環調用觀察者的update()方法的時候
,傳入的參數不同了。
public abstract class Subject {/*** 用來保存注冊的觀察者對象*/private List<Observer> list = new ArrayList<Observer>();/*** 注冊觀察者對象* @param observer 觀察者對象*/public void attach(Observer observer){list.add(observer);System.out.println("Attached an observer");}/*** 刪除觀察者對象* @param observer 觀察者對象*/public void detach(Observer observer){list.remove(observer);}/*** 通知所有注冊的觀察者對象*/public void nodifyObservers(){for(Observer observer : list){observer.update(this);}} }
拉模型的具體主題類
跟推模型相比,有一點變化,就是調用通知觀察者的方法的時候,不需要傳入參數了。
public class ConcreteSubject extends Subject{private String state;public String getState() {return state;}public void change(String newState){state = newState;System.out.println("主題狀態為:" + state);//狀態發生改變,通知各個觀察者this.nodifyObservers();} }
兩種模式的比較
■ 推模型是假定主題對象知道觀察者需要的數據;而拉模型是主題對象不知道觀察者具體需要什么數據,沒有辦法的情況下,干脆把自身傳遞
給觀察者,讓觀察者自己去按需要取值。
■ 推模型可能會使得觀察者對象難以復用,因為觀察者的update()方法是按需要定義的參數,可能無法兼顧沒有考慮到的使用情況。這就意味
著出現新情況的時候,就可能提供新的update()方法,或者是干脆重新實現觀察者;而拉模型就不會造成這樣的情況,因為拉模型下,update()方法的
參數是主題對象本身,這基本上是主題對象能傳遞的最大數據集合了,基本上可以適應各種情況的需要。
JAVA提供的對觀察者模式的支持
在JAVA語言的java.util庫里面,提供了一個Observable類以及一個Observer接口,構成JAVA語言對觀察者模式的支持。
Observer接口
這個接口只定義了一個方法,即update()方法,當被觀察者對象的狀態發生變化時,被觀察者對象的notifyObservers()方法就會調用這一方法。
public interface Observer {void update(Observable o, Object arg); }
Observable類
被觀察者類都是java.util.Observable類的子類。java.util.Observable提供公開的方法支持觀察者對象,這些方法中有兩個對Observable的子類非常
重要:一個是setChanged(),另一個是notifyObservers()。第一方法setChanged()被調用之后會設置一個內部標記變量,代表被觀察者對象的狀態發生
了變化。第二個是notifyObservers(),這個方法被調用時,會調用所有登記過的觀察者對象的update()方法,使這些觀察者對象可以更新自己。
public class Observable {private boolean changed = false;private Vector obs;/** Construct an Observable with zero Observers. */public Observable() {obs = new Vector();}/*** 將一個觀察者添加到觀察者聚集上面*/public synchronized void addObserver(Observer o) {if (o == null)throw new NullPointerException();if (!obs.contains(o)) {obs.addElement(o);}}/*** 將一個觀察者從觀察者聚集上刪除*/public synchronized void deleteObserver(Observer o) {obs.removeElement(o);}public void notifyObservers() {notifyObservers(null);}/*** 如果本對象有變化(那時hasChanged 方法會返回true)* 調用本方法通知所有登記的觀察者,即調用它們的update()方法* 傳入this和arg作為參數*/public void notifyObservers(Object arg) {Object[] arrLocal;synchronized (this) {if (!changed)return;arrLocal = obs.toArray();clearChanged();}for (int i = arrLocal.length-1; i>=0; i--)((Observer)arrLocal[i]).update(this, arg);}/*** 將觀察者聚集清空*/public synchronized void deleteObservers() {obs.removeAllElements();}/*** 將“已變化”設置為true*/protected synchronized void setChanged() {changed = true;}/*** 將“已變化”重置為false*/protected synchronized void clearChanged() {changed = false;}/*** 檢測本對象是否已變化*/public synchronized boolean hasChanged() {return changed;}/*** Returns the number of observers of this <tt>Observable</tt> object.** @return the number of observers of this object.*/public synchronized int countObservers() {return obs.size();} }
這個類代表一個被觀察者對象,有時稱之為主題對象。一個被觀察者對象可以有數個觀察者對象,每個觀察者對象都是實現Observer接口的對象。
在被觀察者發生變化時,會調用Observable的notifyObservers()方法,此方法調用所有的具體觀察者的update()方法,從而使所有的觀察者都被通知更
新自己。
怎樣使用JAVA對觀察者模式的支持
這里給出一個非常簡單的例子,說明怎樣使用JAVA所提供的對觀察者模式的支持。在這個例子中,被觀察對象叫做Watched;而觀察者對象叫做
Watcher。Watched對象繼承自java.util.Observable類;而Watcher對象實現了java.util.Observer接口。另外有一個Test類扮演客戶端角色。
源代碼
被觀察者Watched類源代碼
public class Watched extends Observable{private String data = "";public String getData() {return data;}public void setData(String data) {if(!this.data.equals(data)){this.data = data;setChanged();}notifyObservers();} }
?
觀察者類源代碼
public class Watcher implements Observer{public Watcher(Observable o){o.addObserver(this);}@Overridepublic void update(Observable o, Object arg) {System.out.println("狀態發生改變:" + ((Watched)o).getData());}}
測試類源代碼
public class Test {public static void main(String[] args) {//創建被觀察者對象Watched watched = new Watched();//創建觀察者對象,并將被觀察者對象登記Observer watcher = new Watcher(watched);//給被觀察者狀態賦值watched.setData("start");watched.setData("run");watched.setData("stop");}}
Test對象首先創建了Watched和Watcher對象。在創建Watcher對象時,將Watched對象作為參數傳入;然后Test對象調用Watched對象的
setData()方法,觸發Watched對象的內部狀態變化;Watched對象進而通知實現登記過的Watcher對象,也就是調用它的update()方法。