觀察者模式(有時又被稱爲發佈(publish )-訂閱(Subscribe)模式、模型-視圖(View)模式、源-收聽者(Listener)模式或從屬者模式)是軟件設計模式的一種。在此種模式中,一個目標物件管理所有相依於它的觀察者物件,並且在它本身的狀態改變時主動發出通知。這通常透過呼叫各觀察者所提供的方法來實現。此種模式通常被用來實現事件處理系統。
觀察者模式(Observer)完美的將觀察者和被觀察的對象分離開。舉個例子,用戶界面可以作爲一個觀察者,業務數據是被觀察者,用戶界面觀察業務數據的變化,發現數據變化後,就顯示在界面上。面向對象設計的一個原則是:系統中的每個類將重點放在某一個功能上,而不是其他方面。一個對象只做一件事情,並且將他做好。觀察者模式在模塊之間劃定了清晰的界限,提高了應用程序的可維護性和重用性。
優點
觀察者模式解除了主題和具體觀察者的耦合,讓耦合的雙方都依賴於抽象,而不是依賴具體。從而使得各自的變化都不會影響另一邊的變化。
缺點
依賴關係並未完全解除,抽象通知者依舊依賴抽象的觀察者。
適用場景
當一個對象的改變需要給變其它對象時,而且它不知道具體有多少個對象有待改變時。
一個抽象某型有兩個方面,當其中一個方面依賴於另一個方面,這時用觀察者模式可以將這兩者封裝在獨立的對象中使它們各自獨立地改變和複用。
模式中的角色
抽象主題(Subject):它把所有觀察者對象的引用保存到一個聚集裏,每個主題都可以有任何數量的觀察者。抽象主題提供一個接口,可以增加和刪除觀察者對象。
具體主題(ConcreteSubject):將有關狀態存入具體觀察者對象;在具體主題內部狀態改變時,給所有登記過的觀察者發出通知。
抽象觀察者(Observer):爲所有的具體觀察者定義一個接口,在得到主題通知時更新自己。
具體觀察者(ConcreteObserver):實現抽象觀察者角色所要求的更新接口,以便使本身的狀態與主題狀態協調。
下面是觀察者模式的通用代碼,具體業務可以在此基礎上修改
import java.util.ArrayList;
import java.util.List;
/**
* 目標對象
* 他知道他的觀察者,並提供添加和刪除觀察者的接口
*
* @author ljt
*
*/
public class Subject {
//保存註冊的觀察者對象
private List<Observer> observers=new ArrayList<Observer>();
// 增加觀察者
public void attach(Observer observer)
{
observers.add(observer);
}
// 移除觀察者
public void detach(Observer observer)
{
observers.remove(observer);
}
// 向註冊的觀察者(們)發出通知
protected void notifyObservers()
{
for (Observer observer : observers) {
observer.update(this);
}
}
}
/**
* 觀察者接口
* 定義一個更新的接口給那些在目標發生改變時被通知的對象
* @author 李江濤
*
*/
public interface Observer {
/**
* 更新目標狀態
* @param subject 傳人目標對象,方便獲取相應的目標對象的狀態
*/
void update(Subject subject);
}
/**
* 具體的目標對象
* 他負責把有關狀態存入到相應的觀察者對象中
* @author 李江濤
*
*/
public class ConcreteSubject extends Subject {
//目標狀態
private String subjectState;
public String getSubjectState() {
return subjectState;
}
public void setSubjectState(String subjectState) {
this.subjectState = subjectState;
this.notifyObservers();
}
}
/**
* 具體的觀察對象
* 實現更新的方法,使自己和目標對象保持一致
*
* @author 李江濤
*
*/
public class ConcreteObserver implements Observer {
private String observerState;
/**
* 獲取目標狀態,同步到觀察者的狀態中
*/
@Override
public void update(Subject subject) {
observerState=((ConcreteSubject)subject).getSubjectState();
}
}
下面是以訂閱天氣預報的實例:
/**
* 具體的觀察對象
* 實現更新的方法,使自己和目標對象保持一致
*
* @author 李江濤
*
*/
public class ConcreteObserver implements Observer {
//觀察者姓名
private String observerName;
//天氣情況
private String weatherContent;
/**
* 獲取目標狀態,同步到觀察者的狀態中
*/
@Override
public void update(WeatherSubject subject) {
weatherContent=((ConcreteWeatherSubject)subject).getWeatherState();
System.out.println(observerName+" 收到天氣提醒: "+weatherContent);
}
public String getWeatherContent() {
return weatherContent;
}
public String getObserverName() {
return observerName;
}
public void setObserverName(String observerName) {
this.observerName = observerName;
}
}
/**
* 具體的目標對象
* 他負責把有關狀態存入到相應的觀察者對象中
* @author 李江濤
*
*/
public class ConcreteWeatherSubject extends WeatherSubject {
//目標狀態
private String weatherState;
public String getWeatherState() {
return weatherState;
}
public void setWeatherState(String weatherState) {
this.weatherState = weatherState;
}
}
/**
* 觀察者接口
* 定義一個更新的接口給那些在目標發生改變時被通知的對象
* @author 李江濤
*
*/
public interface Observer {
/**
* 更新目標狀態
* @param subject 傳人目標對象,方便獲取相應的目標對象的狀態
*/
void update(WeatherSubject subject);
}
import java.util.ArrayList;
import java.util.List;
/**
* 目標對象
* 他知道他的觀察者,並提供添加和刪除觀察者的接口
*
* @author ljt
*
*/
public abstract class WeatherSubject {
//保存註冊的觀察者對象
private List<Observer> observers=new ArrayList<Observer>();
// 增加觀察者
public void attach(Observer observer)
{
observers.add(observer);
}
// 移除觀察者
public void detach(Observer observer)
{
observers.remove(observer);
}
// 向註冊的觀察者(們)發出通知
public void notifyObservers()
{
for (Observer observer : observers)
{
observer.update(this);
}
}
}
public class TestObserver {
public static void main(String[] args) {
//創建目標對象
ConcreteWeatherSubject weather=new ConcreteWeatherSubject();
weather.setWeatherState("晴轉多雲");
//創建觀察者對象
ConcreteObserver tom=new ConcreteObserver();
tom.setObserverName("Tom");
ConcreteObserver jack=new ConcreteObserver();
jack.setObserverName("Jack");
//註冊觀察者
weather.attach(tom);
weather.attach(jack);
//向目標發佈
weather.notifyObservers();
}
}