跳到主要內容
黯羽輕揚每天積累一點點

設計模式之觀察者模式(Observer Pattern)

免費2015-03-06#Design_Pattern#观察者模式#Observer Strategy

觀察者模式,一句話概括,就是定義並維護對象之間的一對多關係。「定義」是指建立「一」與「多」之間的依賴關係;「維護」是指我們可以動態地解除依賴以及建立新的依賴。

no_mkd

一。什麼是觀察者模式?

把現實世界中的報紙與訂閱者的關係抽象出來就是觀察者模式,一種報紙對應多個訂閱者,訂閱者可以隨時解除訂閱,未訂閱的讀者也可以隨時開始訂閱。一旦有新報紙發布,所有的訂閱者都會收到新內容。

在觀察者模式中,報紙叫做主題 Subject,訂閱者叫做觀察者 Observer,一個 Subject 可以被多個 Observer 關注,Observer 可以隨時解除關注,新的 Observer 也可以隨時關注 Subject。Subject 內容發生改變時,會通知所有的 Observer。

二。舉個例子

很多網路遊戲中都有答題活動,所有參與答題活動的玩家都會同時收到題目資訊(延遲忽略不計),未參與活動的玩家可以中途加入,正在答題的玩家也可以隨時退出。在此例中,遊戲伺服器就是“一”,玩家是“多”,題目資訊就是在它們之間傳遞的訊息。怎樣才能設計出滿足以上要求的類別?不妨試試觀察者模式。

首先,定義 Subject 抽象類別:

package ObserverPattern;

import java.util.ArrayList;

/**

  • @author ayqy
  • 定义 Subject 抽象类

*/ public abstract class Subject { ArrayList<Observer> observers = new ArrayList<Observer>();//观察者列表

/**
 * 注册主题
 * @param o 申请注册该主题的 Observer
 */
public void registSubject(Observer o)
{
	observers.add(o);
}

/**
 * 删除主题
 * @param o
 */
public void reomveSubject(Observer o)
{
	int index = observers.indexOf(o);
	observers.remove(index);
}

/**
 * 通知所有观察者
 * @param arg 该 Subject 想要传递给 Observers 的数据
 */
public void notifyObservers(Object arg)
{
	for(Observer o : observers)
	o.update(this, arg);
}

}

注意,這裡用了抽象類別而沒有用介面,為什麼?因為 Subject 的行為是 fixed 的,並不需要由子類別來擴充。類似的,我們定義 Observer 抽象類別:

package ObserverPattern;

/**

  • @author ayqy
  • 定义 Observer 抽象类

*/ public abstract class Observer { Subject subject = null;//定义该 Observer 所关注的 Subject

public abstract void update(Subject subject, Object arg);//定义 Observer 的更新接口

}

下面開始實現我們的自定義 Subject——GameServer:

package ObserverPattern;

/**

  • @author ayqy
  • 定义 GameServer 类,继承自 Subject 基类,负责发布题目

*/ public class GameServer extends Subject{ Question ques;//定义题目

public Question getQues() {
	return ques;
}

public void setQues(Question q) {
	this.ques = q;
	
	super.notifyObservers(ques);//调用父类方法通知所有 Observer
}

}

P.S.GameServer 類別的成員變數 Question 是對題目資訊的簡單封裝,Question 類別包含題號 no 與題目內容 content 兩部分定義,以及一個 toString 方法,返回題目描述資訊。再實現我們的自定義 Observer——Player:

package ObserverPattern;

/**

  • @author ayqy
  • 定义 PlayerA,继承自 Observer 基类,负责接收新题目

*/ public class PlayerA extends Observer{

public PlayerA(Subject sub)
{
	subject = sub;
}

 @Override
public void update(Subject subject, Object arg) {
	Question q = (Question)arg;
	System.out.println("PlayerA received " + q.toString());
}

}

P.S.為了使類別層次更加清晰,此處並沒有定義 Player 基類別。很容易複製貼上得到 PlayerB 與 PlayerC,不再贅述。

至此,我們的模擬答題活動準備工作已經結束了,下面我們需要定義一個測試類別來展示觀察者模式的魅力。

三。效果示例

定義如下 Test 類別:

package ObserverPattern;

/**

  • @author ayqy
  • 实现一个测试类,模拟网络游戏答题活动(游戏服务器按时更新题目信息并通知所有参与答题的玩家)

*/ public class Test { public static void main(String[] args) { System.out.println("答题活动即将开始。。"); //创建服务器 GameServer gs = new GameServer(); //创建玩家 ABC Observer playerA = new PlayerA(gs); Observer playerB = new PlayerB(gs); Observer playerC = new PlayerC(gs); //为 AB 注册 Subject,C 对答题不感兴趣,拒绝注册 gs.registSubject(playerA); gs.registSubject(playerB); System.out.println("玩家 AB 成功参与答题活动。。"); System.out.println("答题活动正式开始。。"); gs.setQues(new Question(1, "第一题")); gs.setQues(new Question(2, "第二题")); System.out.println("玩家 A 不想玩了,退出答题活动。。"); gs.reomveSubject(playerA); gs.setQues(new Question(3, "第三题")); System.out.println("玩家 C 想中途加入活动"); gs.registSubject(playerC); gs.setQues(new Question(4, "第四题")); System.out.println("答题活动结束。。"); } }

結果示例:

四。總結

從上面的例子可以看出觀察者模式的特點:

  1. 利用觀察者模式可以輕易地建立對象之間“一對多”的依賴關係;
  2. 利用觀察者模式的機制可以很容易的實現這種依賴關係的動態維護

評論

暫無評論,快來發表你的看法吧

提交評論