1. 程式人生 > >設計模式實戰應用之二:觀察者模式

設計模式實戰應用之二:觀察者模式

        觀察者模式的定義
        觀察者模式是應用最普遍的設計模式之一。著名的 MVC 模式就是觀察者模式的應用之一;高效能網路應用框架 MINA 中的事件處理器也是觀察者模式的應用之一,一旦有 I/O 事件,所有註冊的 IoHandler 物件會被通知到,我們可以通過對這個介面的自定義擴充套件以實現相應業務處理;流媒體服務 Wowza Streaming Engine 的外掛模組擴充套件開發也正是利用到了觀察者模式,使用者通過自定義模組對比如 com.wowza.wms.stream.IMediaStreamActionNotify 等介面的實現,可以捕捉到自己所關心的流的一系列事件,進而就可以
對特定直播/點播頻道進行監控
了。Gof 把觀察者模式歸類到物件行為型模式,《設計模式:可複用面向物件軟體的基礎》對觀察者模式做出了明確的定義:“Define a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically.” 翻譯過來就是:“定義了一些物件之間的一對多依賴關係,這樣子當一個物件的狀態發生變化時,所有依賴於這個物件的物件都會得到通知,並被自動更新”。
        why 觀察者模式?

        把系統設計成一些互相合作的類有一個常見的弊端:需要維護相關物件之間的一致性。為了維護一致性而使各類緊密耦合,大大降低了模組的可複用性,這是我們不願意看到的。
        觀察者模式的使用場合
  • 當一個抽象模型具有一方依賴於另一方的兩方面時,將它們封裝在獨立的物件中可以讓你對它們獨立地改變和複用。
  • 當對於一個物件的改變需要同時改變其他物件,而且你不知道有多少物件需要改變。
  • 當一個物件需要通知其他物件,又不能假定其他物件是誰,換句話說,你不希望這些物件緊密地耦合在一起。
        《多執行緒高效讀取緩衝區資料》需求
        本文示例摒棄了那些枯燥無味、與我們專案毫無關聯的鴨子啊報社啊葡萄園之類的,同時也因為抄來抄去而被用爛了的無聊話題,採用的是 CSDN 網友專案實戰中真實遇到而提出來的一個問題作為案例進行分析。讓我們切實感受設計模式帶來的好處,領略設計模式的真正的威力,而不只是用來玩理論、侃大山。
        這個是網友 xgPaul 發帖提出的一個討論。標題是《如何實現多執行緒高效的讀取一塊緩衝區中的資料???》帖子連結是:
http://bbs.csdn.net/topics/390658695

        帖子正文描述如下:
        一條執行緒不斷的對一塊記憶體緩衝區進行寫資料,同時幾十條執行緒(幾百個物件)要從該緩衝區中讀取資料。這一過程如何實現高效與資料同步。(使用鎖同步效率太低)
        《多執行緒高效讀取緩衝區資料》分析
        xgPaul 遇到的這個問題,比較類似於上海搶拍車牌號的場景:一條執行緒不斷地對當前價格進行重新整理,同時幾十條執行緒(幾百個物件)對當前價格進行讀取監控。用觀察者模式效率比較好,可以解決由於執行緒競爭、加鎖而帶來的效率問題。把讀資料的執行緒歸為觀察者,主題是緩衝區資料。一旦資料有更新,主題向觀察者推送更新資料,這樣推資料的做法效率很高。緩衝區做成主題,每個觀察者都有一份自己關心的主題資料的本地備份,如果主題沒有推資料過來,本地備份就是最新資料。當然,這麼幹稍耗空間,但是卻換得多執行緒環境中效率上的大幅度提升,這就是所謂的用空間換時間。
        java.lang.ThreadLocal 就是這種原理的一個實現。
        《多執行緒高效讀取緩衝區資料》類設計
        本文重在講解模式,所以僅以一個 int 型別的資料 onlinePlayersNum 模擬緩衝區資料。兩個釋出板,一個公開發布板一個內部發布板作為觀察者。
        由 BufferData 物件向兩個釋出板推資料,後者不需要去前者查詢資料,只需要查詢自己的狀態是否改變以決定是否更新發布。具體類圖設計如下:
觀察者模式類圖
        《多執行緒高效讀取緩衝區資料》時序圖
觀察者模式時序圖
        《多執行緒高效讀取緩衝區資料》原始碼實現
        《設計模式:可複用面向物件軟體的基礎》的觀察者模式中的 Subject 角色,在本文中是為 Subject 介面,原始碼:
package com.defonds.buffer;

public interface Subject {
	public void registerObserver(Observer observer);
	public void removeObserver(Observer observer);
	public void notifyObservers();
}

        《設計模式:可複用面向物件軟體的基礎》觀察者模式中的 Observer 角色,在本文中是為 Observer 介面,原始碼:
package com.defonds.buffer;

public interface Observer {
	public void update(int onlineNum);
}

        《設計模式:可複用面向物件軟體的基礎》觀察者模式中的 ConcreteSubject 角色,在本文中是為 BufferData 類,原始碼:
package com.defonds.buffer;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

public class BufferData implements Subject {
	private int onlinePlayersNum = 0; // store online players num
	private List<Observer> observers = new ArrayList<Observer>(); // refer to all observers
	
	@Override
	public void registerObserver(Observer observer) {
		this.observers.add(observer); // add one observer
		
	}

	@Override
	public void removeObserver(Observer observer) {
		int i = this.observers.indexOf(observer);
		if (i >= 0) {
			this.observers.remove(i);
		}

	}

	@Override
	public void notifyObservers() {
		Iterator<Observer> iterator = this.observers.iterator();
		while (iterator.hasNext()) {
			iterator.next().update(this.onlinePlayersNum);
		}
	}

	public void setOnlinePlayersNum(int onlinePlayersNum) {
		this.onlinePlayersNum = onlinePlayersNum;
		this.notifyObservers();
	}
	
	

}

        《設計模式:可複用面向物件軟體的基礎》觀察者模式中的 ConcreteObserver 角色之一,在本文中是為 PublicDisplay 類,原始碼:
package com.defonds.buffer;

public class PublicDisplay extends Thread implements Observer, Display {
	private int onlineNum;
	private boolean changed = false;

	@Override
	public void update(int onlineNum) {
		this.onlineNum = onlineNum;
		this.changed = true;
	}
	
	@Override
	public void display() {
		System.out.println("The current number of players online is : " + this.onlineNum);
	}

	@Override
	public void run() {
		while (this.changed) {
			this.display();
			this.changed = false;
		}
	}
}

        《設計模式:可複用面向物件軟體的基礎》觀察者模式中的 ConcreteObserver 角色之二,在本文中是為 InternalDisplay 類,原始碼:

package com.defonds.buffer;

public class InternalDisplay extends Thread implements Observer, Display {
	private int oldNum = 0;
	private int onlineNum = 0;
	private boolean changed = false;

	@Override
	public void update(int onlineNum) {
		this.oldNum = this.onlineNum;
		this.onlineNum = onlineNum;
		this.changed = true;
	}
	
	@Override
	public void display() {
		System.out.println("The current number of players online is : " + this.onlineNum);
		if(this.onlineNum > this.oldNum) {
			System.out.println("The number of online upward");
		} else {
			System.out.println("The number of online decline");
		}
	}
	
	@Override
	public void run() {
		while (this.changed) {
			this.display();
			this.changed = false;
		}
	}

}