Observer模式也叫觀察者模式,是由GoF提出的23種軟件設計模式的一種。Observer模式是行為模式之一,它的作用是當一個對象的狀態發生變化時,能夠自動通知其他關聯對象,自動刷新對象狀態。
本文介紹設計模式中的(Observer)模式的概念,用法,以及實際應用中怎麼樣使用Observer模式進行開發。
Observer模式的概念
Observer模式是行為模式之一,它的作用是當一個對象的狀態發生變化時,能夠自動通知其他關聯對象,自動刷新對象狀態。
Observer模式提供給關聯對象一種同步通信的手段,使某個對象與依賴它的其他對象之間保持狀態同步。
Observer模式的典型應用
- 偵聽事件驅動程序設計中的外部事件
- 偵聽/監視某個對象的狀態變化
- 發布者/訂閱者(publisher/subscriber)模型中,當一個外部事件(新的產品,消息的出現等等)被觸發時,通知郵件列表中的訂閱者
Observer模式的優點
- 對象之間可以進行同步通信
- 可以同時通知一到多個關聯對象
- 對象之間的關系以松耦合的形式組合,互不依賴
Observer模式的結構
Observer模式的角色:
Subject(被觀察者)
被觀察的對象。當需要被觀察的狀態發生變化時,需要通知隊列中所有觀察者對象。Subject需要維持(添加,刪除,通知)一個觀察者對象的隊列列表。
ConcreteSubject
被觀察者的具體實現。包含一些基本的屬性狀態及其他操作。
Observer(觀察者)
接口或抽象類。當Subject的狀態發生變化時,Observer對象將通過一個callback函數得到通知。
ConcreteObserver
觀察者的具體實現。得到通知後將完成一些具體的業務邏輯處理。
JDK對Observer模式的支持
JDK提供了對observer設計模式的支持:
- 通過java.util.Observable類扮演Subject角色,一個類只需通過繼承java.util.Observable類便可擔當ConcreteSubject角色;
- 通過java.util.Observer接口扮演Observer角色,一個類只需實現java.util.Observer接口便可擔當ConcreteObserver角色。
- java.util.Observable的實現類通過調用setChanged()方法以及notifyObservers(Object)方法便可簡單地通知Observer。
當然,我們也可以不使用JDK提供的類與接口而自己實現Observer模式。
Observer模式的應用范例
下面,我們使用JDK提供的接口與類來實現一個Observer模式用來模擬新聞訂閱操作:
當一個新聞被發表時,系統一方面需要通知管理者審閱;另一方面需要通知訂閱者。
文件一覽:
Client
測試類。
NewsPublisher
相當於ConcreteSubject角色。該類繼承相當於Subject角色的java.util.Observable類。
SubscriberObserver
相當於ConcreteObserver角色。該類實現了相當於Observer角色的java.util.Observer接口。當NewsPublisher對象發生變化時得到通知,並向訂閱者發送訂閱郵件。
ManagerObserver
相當於ConcreteObserver角色。該類實現了相當於Observer角色的java.util.Observer接口。當NewsPublisher對象發生變化時得到通知,並向管理者發送消息郵件。
News
封裝了新聞數據的類,與Observer模式無直接關系。
代碼:
import java.util.Observable;
import java.util.Observer;
public class Client {
/**
* Test Observer Pattern
* --
*
*/
public static void main(String[] args) {
NewsPublisher publisher = new NewsPublisher();
//添加觀察者對象
publisher.addObserver(new SubscriberObserver());
publisher.addObserver(new ManagerObserver());
//發布新聞,觸發通知事件
publisher.publishNews("Hello news", "news body");
}
}
/**
* Subject ROLE
* NewsPublisher: news publisher
*
*/
class NewsPublisher extends Observable {
public void publishNews(String newsTitle, String newsBody) {
News news = new News(newsTitle, newsBody);
setChanged(); //通過setChanged()方法標明對象的狀態已發生變化
System.out.println("News published:" + newsTitle);
this.notifyObservers(news); //通知各Observer,並發送一個名為news對象的消息
//other process ... such as save news to database
}
}
/**
* Observer ROLE
*
*/
class SubscriberObserver implements Observer {
public void update(Observable observee, Object param) {
if (param instanceof News) {
mail2Subscriber((News)param);
}
}
private void mail2Subscriber(News news) {
System.out.println("Mail to subscriber. A news published with title:" + news.getTitle());
}
}
/**
* Observer ROLE
*
*/
class ManagerObserver implements Observer {
public void update(Observable observee, Object param) {
if (param instanceof News) {
mail2Manager((News)param);
}
}
private void mail2Manager(News news) {
System.out.println("Mail to Manager. A news published with title:" + news.getTitle());
}
}
//data entity
class News {
private String title;
private String body;
public News(String title, String body) {
this.title = title;
this.body = body;
}
public String getBody() {
return body;
}
public void setBody(String body) {
this.body = body;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
}
執行Client,輸出結果:
C:Observer>javac *.java
C:Observer>java Client
News published:Hello news
Mail to Manager. A news published with title:Hello news
Mail to subscriber. A news published with title:Hello news
C:Observer>
NewsPublisher通過setChanged()與notifyObservers(Object)方法通知Observer對象SubscriberObserver與ManagerObserver。
不適用jdk支持的情況下的代碼
Observer模式的好處是:它解除了觀察者和目標之間的耦合關系。目標不需要知道它的觀察者的任何信息。相反,目標只是允許觀察者訂閱事件。當目標產生一個事件時,它簡單地將事件傳給每一個觀察者。
看看下面的Java示例:
public interface Subject {
public void addObserver( Observer o );
public void removeObserver( Observer o );
}
上面的代碼中,Subject接口定義了兩個方法(method),每個Subject都必須實現它們,以使Observer可以在Subject中增加或刪除自身。
public interface Observer {
public void update( Subject o );
}
Observer接口(如上)列出了一個方法(method),每個Observer都必須實現它,以使Subject可以發送更新消息給Observer。
下面看看Subject的一個簡單的實現--IntegerDataBag:
import java.util.ArrayList;
import java.util.Iterator;
public class IntegerDataBag implements Subject {
private ArrayList list = new ArrayList();
private ArrayList observers = new ArrayList();
public void add( Integer i ) {
list.add( i );
notifyObservers();
}
public Iterator iterator() {
return list.iterator();
}
public Integer remove( int index ) {
if( index < list.size() ) {
Integer i = (Integer) list.remove( index );
notifyObservers();
return i;
}
return null;
}
public void addObserver( Observer o ) {
observers.add( o );
}
public void removeObserver( Observer o ) {
observers.remove( o );
}
private void notifyObservers() {
// loop through and notify each observer
Iterator i = observers.iterator();
while( i.hasNext() ) {
Observer o = ( Observer ) i.next();
o.update( this );
}
}
}
IntegerDataBag適用於使用Integer的場合。IntegerDataBag也允許Observer增加和刪除它們自身。
再看看兩個Observer的實現--IntegerAdder和IntegerPrinter:
import java.util.Iterator;
public class IntegerAdder implements Observer {
private IntegerDataBag bag;
public IntegerAdder( IntegerDataBag bag ) {
this.bag = bag;
bag.addObserver( this );
}
public void update( Subject o ) {
if( o == bag ) {
System.out.println( "The contents of the IntegerDataBag have changed." );
int counter = 0;
Iterator i = bag.iterator();
while( i.hasNext() ) {
Integer integer = ( Integer ) i.next();
counter+=integer.intValue();
}
System.out.println( "The new sum of the integers is: " + counter );
}
}
}
import java.util.Iterator;
public class IntegerPrinter implements Observer {
private IntegerDataBag bag;
public IntegerPrinter( IntegerDataBag bag ) {
this.bag = bag;
bag.addObserver( this );
}
public void update( Subject o ) {
if( o == bag ) {
System.out.println( "The contents of the IntegerDataBag have changed." );
System.out.println( "The new contents of the IntegerDataBag contains:" );
Iterator i = bag.iterator();
while( i.hasNext() ) {
System.out.println( i.next() );
}
}
}
}
IntegerAdder和IntegerPrinter將自己作為觀察者增加到IntegerDataBag。當IntegerAdder接收到一條更新消息時,它先統計bag中的總數,然後顯示結果。同樣,當IntegerPrinter接收到一條更新消息時,它打印出bag中的 Interger。
下面是一個簡單的main(),它使用了上面的幾個類:
public class Driver {
public static void main( String [] args ) {
Integer i1 = new Integer( 1 ); Integer i2 = new Integer( 2 );
Integer i3 = new Integer( 3 ); Integer i4 = new Integer( 4 );
Integer i5 = new Integer( 5 ); Integer i6 = new Integer( 6 );
Integer i7 = new Integer( 7 ); Integer i8 = new Integer( 8 );
Integer i9 = new Integer( 9 );
IntegerDataBag bag = new IntegerDataBag();
bag.add( i1 ); bag.add( i2 ); bag.add( i3 ); bag.add( i4 );
bag.add( i5 ); bag.add( i6 ); bag.add( i7 ); bag.add( i8 );
IntegerAdder adder = new IntegerAdder( bag );
IntegerPrinter printer = new IntegerPrinter( bag );
// adder and printer add themselves to the bag
System.out.println( "About to add another integer to the bag:" );
bag.add( i9 );
System.out.println("");
System.out.println("About to remove an integer from the bag:");
bag.remove( 0 );
}
}
運行main,你將看到:
c:javaworldjava Driver
About to add another integer to the bag:
The contents of the IntegerDataBag have changed.
The new sum of the intergers is: 45
The contents of the IntegerDataBag have changed.
The new contents of the IntegerDataBag contains:
1
2
3
4
5
6
7
8
9
About to remove an integer from the bag:
The contents of the IntegerDataBag have changed.
The new sum of the intergers is: 44
The contents of the IntegerDataBag have changed.
The new contents of the IntegerDataBag contains:
2
3
4
5
6
7
8
9
IntegerDataBag/IntegerAdder/IntegerPrinter是應用Observer模式的一個很簡單的例子。Java 本身有大量使用Observer模式的例子:AWT/Swing事件模型,還有java.util.Observer和 java.util.Observable接口等,都是很好的例子。