“內容摘自《設計模式 可複用面向對象軟件的基礎》
類型
行爲型模式
意圖
定義對象間的一種一對多的依賴關係,當一個對象的狀態發生改變時,所有依賴於它的對象都得到通知並被自動更新。
適用性
當出現以下情況時可考慮使用該模式:
-
一個抽象模型有兩個方面,其中一個方面依賴於另一個方面。將二者獨立封裝在各自的類中,使其可以獨立的改變和複用。 -
對一個對象的改變必須要同時改變其他對象,而不知道具體有多少對象需要被改變。 -
一個對象必須通知其他對象,又不能假定其他對象是誰,換言之就是不希望這些對象緊密耦合在一起。
參與者
-
Subject(目標)
目標知道它的觀察者。一個目標可以有任意多個觀察者。
提供註冊和刪除觀察者對象的接口。 -
Observer(觀察者)
爲那些在目標發生改變時需要獲得通知的對象定義一個更新接口。 -
ConcreteSubject(具體目標)
將有關狀態存入各ConcreteObserver對象。
當它的對象發生改變時,向其各個觀察者發出通知。 -
ConcreteObserver(具體觀察者)
維護一個指向ConcreteSubject的引用。
存儲有關狀態,這些狀態應與目標的狀態保持一致。
實現Observer的更新接口,以使自身狀態與目標狀態保持一致。
協作
-
當ConcreteSubject發生任何可能導致其觀察者與其本身狀態不一致的改變時,它將通知它的各個觀察者。 -
在得到一個具體的改變通知後,ConcreteObserver對象可向目標對象查詢信息。ConcreteObserver使用這些信息以使它的狀態與目標對象的狀態一致。
效果
-
目標和觀察者間的抽象耦合
對於目標而言,只知道自己有一系列Observer,不知道其具體的類,這使得目標與觀察者的耦合變得最小。 -
支持廣播通信
目標發送的通知,不需要指定它的接收者,只需要通知登記了的觀察者對象,目標對象也不感興趣有多少對象對自己感興趣,僅有的責任就是通知所有觀察者。這帶來了在任何時刻增刪觀察者的自由,如何處理通知,由每一個觀察者自己決定。 -
意外的更新
因爲觀察者不知道有其他觀察者存在,對於改變目標狀態的代價一無所知,這可能會導致其他依賴於目標狀態的一系列觀察者出現意外的更新,而且難以捕捉。
Demo
假設飛機存在起飛、飛行中、降落三種信號狀態,乘客、空乘、地勤人員會關注這些狀態,每當狀態改變時,三種人員會做出對應的行爲。
/**
* 信號
*/
public abstract class Signal {
private List<Observer> observers;
public Signal() {
this.observers = new ArrayList<Observer>();
}
public void attach(Observer observer) {
observers.add(observer);
}
public void detach(Observer observer) {
observers.remove(observer);
}
public void notifyObserver() {
observers.stream().forEach(o -> {
o.prepare();
});
}
}
/**
* 飛機指示信號
*/
public class PlaneSignal extends Signal {
// blue-準備起飛;yellow-準備降落;white-飛行中;
private String colorSignal;
public String getColorSignal() {
return colorSignal;
}
public void setColorSignal(String colorSignal) {
this.colorSignal = colorSignal;
notifyObserver();
}
}
public abstract class Observer {
protected Signal signal;
public void subscribe(Signal sig) {
this.signal = sig;
signal.attach(this);
}
public void unSubscribe(Signal sig) {
sig.detach(this);
}
abstract void prepare();
}
/**
* 乘客
*/
public class Passenger extends Observer {
@Override
void prepare() {
PlaneSignal sig = (PlaneSignal) signal;
System.out.print("乘客:");
switch (sig.getColorSignal()) {
case "blue":
System.out.println("飛機準備起飛,我要放好行李,綁好安全帶");
break;
case "yellow":
System.out.println("飛機準備降落,綁好安全帶,確認走時不會忘記行李");
break;
default:
System.out.println("飛機飛行中,打個盹兒吧");
break;
}
}
}
/**
* 空乘
*/
public class Stewardess extends Observer {
@Override
void prepare() {
PlaneSignal sig = (PlaneSignal) signal;
System.out.print("空乘:");
switch (sig.getColorSignal()) {
case "blue":
System.out.println("飛機準備起飛,我要提醒乘客關機,綁好安全帶");
break;
case "yellow":
System.out.println("飛機準備降落,我要提醒乘客,不要忘記行李");
break;
default:
System.out.println("飛機飛行中,我要去進行派餐服務");
break;
}
}
}
/**
* 地勤人員
*/
public class GroundStaff extends Observer {
@Override
void prepare() {
PlaneSignal sig = (PlaneSignal) signal;
System.out.print("地勤:");
switch (sig.getColorSignal()) {
case "blue":
System.out.println("飛機準備起飛,我要快點把托運行李放好");
break;
case "yellow":
System.out.println("飛機準備降落,我要準備接機,運輸行李");
break;
default:
System.out.println("飛機飛行中,下一班飛機還有一會,摸個魚吧");
break;
}
}
}
主程序
public class App {
public static void main(String[] args) {
PlaneSignal signal = new PlaneSignal();
Passenger passenger = new Passenger();
Stewardess stewardess = new Stewardess();
GroundStaff groundStaff = new GroundStaff();
passenger.subscribe(signal);
stewardess.subscribe(signal);
groundStaff.subscribe(signal);
System.out.println("--------3個觀察者,準備起飛--------");
signal.setColorSignal("blue");
System.out.println("--------2個觀察者,準備降落--------");
passenger.unSubscribe(signal);
signal.setColorSignal("yellow");
System.out.println("--------1個觀察者,飛行中--------");
groundStaff.unSubscribe(signal);
signal.setColorSignal("white");
}
}
結果爲: