概念
觀察者模式又叫做發佈-訂閱模式,定義了對象間一對多的依賴關係,使得當對象狀態發生變化時,所有依賴它的對象都會收到通知並且自動更新自己。
特點
1)被觀察者需要持有一個或者多個觀察者對象。
2)系統中一個模塊的變化,某些模塊也會跟隨着變化。
角色
觀察者設計模式涉及到兩種角色:主題(Subject)和觀察者(Observer)
(1)Subject模塊
Subjec模塊有3個主要操作
①addObserver():註冊添加觀察者(申請訂閱)
②deleteObserver():刪除觀察者(取消訂閱)
③notifyObserver():主題狀態發生變化時通知所有的觀察者對象
(2)Oserver模塊
Oserver模塊有1個核心操作update(),當主題Subject狀態改變時,將調用每個觀察者的update()方法,更新通知。
類圖
從上面的UML可以看出來,觀察者模式設計到的角色有如下四個:
- 抽象被觀察者角色:-定義了動態增加、刪除以及通知觀察者對象的方法,職責就是管理和通知觀察者。持有觀察者對象的集合。
**- 具體被觀察者角色:-**一般繼承抽象被觀察者,實現自己本身的業務邏輯,當狀態發生改變時發起通知。
**- 抽象觀察者角色:-**提供一個接口,定義了觀察者收到通知時更新自己的方法。
**- 具體觀察者角色:**實現抽象觀察者接口,處理不同具體觀察者的不同業務邏輯。
案例一
package com.hanker.annotation;
import java.util.ArrayList;
import java.util.List;
//主題接口
interface Subject {
//添加觀察者
void addObserver(Observer obj);
//移除觀察者
void deleteObserver(Observer obj);
//當主題方法改變時,這個方法被調用,通知所有的觀察者
void notifyObserver();
}
//觀察者接口
interface Observer {
//當主題狀態改變時,更新通知
public void update(int version);
}
//主題實現類,某某雜誌
class MagazineSubject implements Subject{
//存儲所有的觀察者
private List<Observer> list = new ArrayList<>();
//期刊版本
private int version;
//添加觀察者
@Override
public void addObserver(Observer obj) {
list.add(obj);
}
//刪除觀察者
@Override
public void deleteObserver(Observer obj) {
list.remove(obj);
}
//通知觀察者
@Override
public void notifyObserver() {
for(Observer o : list) {
o.update(version);//調用觀察者的更新方法
}
}
//該雜誌發行了新版本
public void publish() {
//新版本
this.version ++;
//信息更新完畢,通知所有觀察者
notifyObserver();
}
}
//觀察者實現類
class CustomerObserver implements Observer{
//訂閱者名字
private String name;
private int version;
public CustomerObserver(String name) {
this.name =name;
}
@Override
public void update(int version) {
this.version = version;
System.out.println("雜誌出新版本了...");
this.buy();
}
public void buy() {
System.out.println(name + "購買了第"+version +"期的雜誌.");
}
}
public class ObserverPatternDemo1 {
public static void main(String[] args) {
// 創建主題(被觀察者)
MagazineSubject subject = new MagazineSubject();
//創建三個不同的觀察者
CustomerObserver c1 = new CustomerObserver("迪巴");
CustomerObserver c2 = new CustomerObserver("古扎");
CustomerObserver c3 = new CustomerObserver("哈孜");
//將觀察者註冊到主題中
subject.addObserver(c1);
subject.addObserver(c2);
subject.addObserver(c3);
//更新主題的數據,當數據更新後,會自動通知所有已註冊的觀察者
subject.publish();
}
}
案例二
使用Java自帶的觀察者模式類實現
上面自己通過接口的方式,實現了一個觀察者模式,但是Java自身也是有這方面的實現了,下面看看如何用Java自帶的實現一個觀察者模式。在 java.util 裏爲觀察者模式提供了 一個Observable類(給數據源繼承) 和 一個 Observer接口(給觀察者實現):import java.util.Observable;import java.util.Observer;
Observable類中的方法,java已經幫我們實現好了。可以直接調用
1.定義具體被觀察者
import java.util.Observable;
/**
* 目標對象繼承 Observable
*/
public class ConcreteSubject extends Observable {
private int state;
public void set(int s){
state = s; //目標對象的狀態發生了改變
setChanged(); //表示目標對象已經做了更改
notifyObservers(state); //通知所有的觀察者
}
public int getState() {
return state;
}
public void setState(int state) {
this.state = state;
}
}
觀察者只需要繼承Observable父類。發送消息的方式執行如下兩行代碼即可
setChanged(); //表示目標對象已經做了更改
notifyObservers(state); //通知所有的觀察者
Observable源碼對應的是:
/**
* Marks this <tt>Observable</tt> object as having been changed; the
* <tt>hasChanged</tt> method will now return <tt>true</tt>.
*/
protected synchronized void setChanged() {
changed = true;
}
/**
* If this object has changed, as indicated by the
* <code>hasChanged</code> method, then notify all of its observers
* and then call the <code>clearChanged</code> method to indicate
* that this object has no longer changed.
* <p>
* Each observer has its <code>update</code> method called with two
* arguments: this observable object and the <code>arg</code> argument.
*
* @param arg any object.
* @see java.util.Observable#clearChanged()
* @see java.util.Observable#hasChanged()
* @see java.util.Observer#update(java.util.Observable, java.lang.Object)
*/
public void notifyObservers(Object arg) {
/*
* a temporary array buffer, used as a snapshot of the state of
* current Observers.
*/
Object[] arrLocal;
synchronized (this) {
/* We don't want the Observer doing callbacks into
* arbitrary code while holding its own Monitor.
* The code where we extract each Observable from
* the Vector and store the state of the Observer
* needs synchronization, but notifying observers
* does not (should not). The worst result of any
* potential race-condition here is that:
* 1) a newly-added Observer will miss a
* notification in progress
* 2) a recently unregistered Observer will be
* wrongly notified when it doesn't care
*/
if (!changed)
return;
arrLocal = obs.toArray();//獲取所有的訂閱者對象
clearChanged();
}
for (int i = arrLocal.length-1; i>=0; i--)
((Observer)arrLocal[i]).update(this, arg);//遍歷所有訂閱者對象,調用update方法
}
2.定義具體觀察者
import java.util.Observable;
import java.util.Observer;
/**
* 觀察者模式:觀察者(消息訂閱者)實現Observer接口
*/
public class ObserverA implements Observer {
private int myState;
@Override
public void update(Observable o, Object arg) {
myState = ((ConcreteSubject)o).getState();
}
public int getMyState() {
return myState;
}
public void setMyState(int myState) {
this.myState = myState;
}
}
觀察者也就是訂閱者只需要實現Observer接口並重寫相關update方法即可,在目標實現中我們發現觸發的時候執行的就是觀察者的update方法。
3.測試
public class Client {
public static void main(String[] args) {
//創建目標對象Obserable
ConcreteSubject subject = new ConcreteSubject();
//創建觀察者
ObserverA obs1 = new ObserverA();
ObserverA obs2 = new ObserverA();
ObserverA obs3 = new ObserverA();
//將上面三個觀察者對象添加到目標對象subject的觀察者容器中
subject.addObserver(obs1);
subject.addObserver(obs2);
subject.addObserver(obs3);
//改變subject對象的狀態
subject.set(3000);
System.out.println("===============狀態修改了!");
//觀察者的狀態發生了變化
System.out.println(obs1.getMyState());
System.out.println(obs2.getMyState());
System.out.println(obs3.getMyState());
subject.set(600);
System.out.println("===============狀態修改了!");
//觀察者的狀態發生了變化
System.out.println(obs1.getMyState());
System.out.println(obs2.getMyState());
System.out.println(obs3.getMyState());
//移除一個訂閱者
subject.deleteObserver(obs2);
subject.set(100);
System.out.println("===============狀態修改了!");
//觀察者的狀態發生了變化
System.out.println(obs1.getMyState());
System.out.println(obs2.getMyState());
System.out.println(obs3.getMyState());
}
}
這樣就實現了官方提供觀察者模式,其實這裏的Observable和Observer和我們前面自定義的Subject和Observer差不多,只是考慮的比較全面罷了。
優缺點
優點
1)觀察者和被觀察者之間抽象耦合。觀察者模式容易擴展,被觀察者只持有觀察者集合,並不需要知道具體觀察者內部的實現。
2)對象之間的保持高度的協作。當被觀察者發生變化時,所有被觀察者都會通知到,然後做出相應的動作。
缺點
1)如果觀察者太多,被觀察者通知觀察者消耗的時間很多,影響系統的性能。
2)當觀察者集合中的某一觀察者錯誤時就會導致系統卡殼,因此一般會採用異步方式。