設計模式之六觀察者模式

概念

觀察者模式又叫做發佈-訂閱模式,定義了對象間一對多的依賴關係,使得當對象狀態發生變化時,所有依賴它的對象都會收到通知並且自動更新自己。

特點

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;

img

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)當觀察者集合中的某一觀察者錯誤時就會導致系統卡殼,因此一般會採用異步方式

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章