简单观察者模式
观察者模式中,一个被观察者管理所有依赖它的观察者,并且在本身的状态改变时主动发出通知。这通常通过呼叫各观察者所提供的方法来实现。此种模式通常被用来实现事件处理系统。
角色
抽象被观察者角色:把所有对观察者对象的引用保存在一个集合中,每个被观察者角色都可以有任意数量的观察者。被观察者提供一个接口,可以增加和删除观察者角色。一般用一个抽象类和接口来实现。
抽象观察者角色:为所有具体的观察者定义一个接口,在得到主题的通知时更新自己。
具体被观察者角色:在被观察者内部状态改变时,给所有登记过的观察者发出通知。具体被观察者角色通常用一个子类实现。
具体观察者角色:该角色实现抽象观察者角色所要求的更新接口,以便使本身的状态与主题的状态相协调。通常用一个子类实现。如果需要,具体观察者角色可以保存一个指向具体主题角色的引用。
适用场景
1) 当一个抽象模型有两个方面, 其中一个方面依赖于另一方面。将这二者封装在独立的对象中以使它们可以各自独立地改变和复用。
2) 当对一个对象的改变需要同时改变其它对象, 而不知道具体有多少对象有待改变。
3) 当一个对象必须通知其它对象,而它又不能假定其它对象是谁。换言之, 你不希望这些对象是紧密耦合的
public interface Watcher
{
public void update();
}
public interface Watched
{
public void addWatcher(Watcher watcher);
public void removeWatcher(Watcher watcher);
public void notifyWatchers();
}
public class Thief implements Watcher
{
@Override
public void update()
{
System.out.println(“运输车有行动,强盗准备动手");
}
}
public class Police implements Watcher
{
@Override
public void update()
{
System.out.println(“运输车有行动,警察护航");
}
}
public class Transporter implements Watched
{
private List<Watcher> list = new ArrayList<Watcher>();
@Override
public void addWatcher(Watcher watcher)
{
list.add(watcher);
}
@Override
public void removeWatcher(Watcher watcher)
{
list.remove(watcher);
}
@Override
public void notifyWatchers(String str)
{
for (Watcher watcher : list)
{
watcher.update();
}
}
}
[java] view plain copy print?在CODE上查看代码片派生到我的代码片
public class Test
{
public static void main(String[] args)
{
Transporter transporter = new Transporter();
Police police = new Police();
Security security = new Security();
Thief thief = new Thief();
transporter.addWatcher(police);
transporter.addWatcher(security);
transporter.addWatcher(security);
transporter.notifyWatchers();
}
}
推模型和拉模型
在观察者模式中,又分为推模型和拉模型两种方式。
● 推模型
主题对象向观察者推送主题的详细信息,不管观察者是否需要,推送的信息通常是主题对象的全部或部分数据。
● 拉模型
主题对象在通知观察者的时候,只传递少量信息。如果观察者需要更具体的信息,由观察者主动到主题对象中获取,相当于是观察者从主题对象中拉数据。一般这种模型的实现中,会把主题对象自身通过update()方法传递给观察者,这样在观察者需要获取数据的时候,就可以通过这个引用来获取了。
最近做一个消息系统,其中涉及到新消息数的即时更新,当时就想到了观察者模式,后来听同事提到推拉模式,感觉推模式原理上应该还是属于观察者模式,只不过把server变成了被观察对象,client被动观察 .
其实推拉模式我们经常遇到,如广播(推)、HTTP请求(拉),只是没有刻意去追求概念。设计时还是应该多考虑到。
考虑到性能还效率,最终还是选择了拉模式,每隔一断时间请求一次、更新。
下面是引用:
推(push)模式是一种基于客户器/服务器机制、由服务器主动将信息送到客户器的技术。在push模式应用中,服务器把信息送给客户器之前,并没有明显的客户请求。push事务由服务器发起。push模式可以让信息主动、快速地寻找用户/客户器,信息的主动性和实时性比较好。但精确性较差,可能推送的信息并不一定满足客户的需求。推送模式不能保证能把信息送到客户器,因为推模式采用了广播机制,如果客户器正好联网并且和服务器在同一个频道上,推送模式才是有效的。push模式无法跟踪状态,采用了开环控制模式,没有用户反馈信息。在实际应用中,由客户器向服务器发送一个申请,并把自己的地址(如IP、port)告知服务器,然后服务器就源源不断地把信息推送到指定地址。在多媒体信息广播中也采用了推模式。另外,如手机*、qq广播。
拉(pull)模式与推模式相反,是由客户器主动发起的事务。服务器把自己所拥有的信息放在指定地址(如IP、port),客户器向指定地址发送请求,把自己需要的资源“拉”回来。不仅可以准确获取自己需要的资源,还可以及时把客户端的状态反馈给服务器。
使用jdk的java.util.Observable
import java.util.Observable;
/**
* 天气目标的具体实现类
* @author lenovo
*
*/
public class ConcreatWeatherSubject extends Observable{
private String content;
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
//通知所有的观察者
this.setChanged();
//推模型:主动通知
this.notifyObservers(content);
//this.notifyObservers();
}
}
import java.util.Observable;
import java.util.Observer;
public class ConcreatObserver implements Observer {
private String observerName;
public String getObserverName() {
return observerName;
}
public void setObserverName(String observerName) {
this.observerName = observerName;
}
/**
* Observable o 拉取方式
* Object arg 推的方式,推具体的内容过来
*/
@Override
public void update(Observable o, Object arg) {
System.out.println(this.observerName+"收到推送消息:"+arg);
System.out.println(this.observerName+"拉去消息中的内容:"+((ConcreatWeatherSubject)o).getContent());
}
}
public class Client {
public static void main(String[] args) {
ConcreatWeatherSubject subject= new ConcreatWeatherSubject();
ConcreatObserver girl = new ConcreatObserver();
girl.setObserverName("king");
ConcreatObserver girl1 = new ConcreatObserver();
girl1.setObserverName("jinhang");
ConcreatObserver girl2 = new ConcreatObserver();
girl2.setObserverName("kee");
subject.addObserver(girl1);
subject.addObserver(girl);
subject.addObserver(girl2);
subject.setContent("天气晴朗!");
}
}
条件消息系统
import java.util.ArrayList;
import java.util.List;
public abstract class WeatherSubject {
public List<Observer> observers = new ArrayList<Observer>();
public void attach(Observer o) {
observers.add(o);
}
public void dettach(Observer o) {
observers.remove(o);
}
protected abstract void notifyObservers();
}
import java.util.Iterator;
public class Subject1 extends WeatherSubject{
private String weatherContent;
public String getWeatherContent() {
return weatherContent;
}
public void setWeatherContent(String weatherContent) {
this.weatherContent = weatherContent;
this.notifyObservers();
}
@Override
protected void notifyObservers() {
for(Observer o: observers){
//通知条件
if("下雨".equals(this.weatherContent)){
System.out.println("notifyObservers");
if("jinhang".equals(o.getObserverName())){
o.update(this);
}
if("kee".equals(o.getObserverName())){
o.update(this);
}
}
if("下雪".equals(this.weatherContent)){
if("king".equals(o.getObserverName())){
o.update(this);
}
}
}
}
}
public interface Observer {
public void update(WeatherSubject subject);
public void setObserverName(String observerName);
public String getObserverName();
}
public class Observer1 implements Observer{
private String observerName;
private String weatherContent;
private String remindContet;
public String getWeatherContent() {
return weatherContent;
}
public void setWeatherContent(String weatherContent) {
this.weatherContent = weatherContent;
}
public String getRemindContet() {
return remindContet;
}
public void setRemindContet(String remindContet) {
this.remindContet = remindContet;
}
@Override
public void update(WeatherSubject subject) {
System.out.println("start...............");
weatherContent = ((Subject1)subject).getWeatherContent();
System.out.println(observerName+"收到消息"+weatherContent+"准备去:"+remindContet);
}
@Override
public void setObserverName(String observerName) {
this.observerName = observerName;
}
@Override
public String getObserverName() {
return observerName;
}
}
public class Test {
public static void main(String[] args) {
Subject1 subject1 = new Subject1();
Observer1 observer1 = new Observer1();
Observer1 observer2 = new Observer1();
observer1.setObserverName("jinhang");
observer1.setRemindContet("购物");
observer2.setObserverName("kee");
observer2.setRemindContet("不出去了");
subject1.attach(observer1);
subject1.attach(observer2);
subject1.setWeatherContent("下雨");
}
}
JUnit为用户提供了三种不同的测试结果显示界面,以后还可能会有其它方式的现实界面……。怎么才能将测试的业务逻辑和显示结果的界面很好的分离开?不用问,就是观察者模式!
public interface TestListener {
/**
* An error occurred.
*/
public void addError(Test test, Throwable t);
/**
* A failure occurred.
*/
public void addFailure(Test test, AssertionFailedError t);
/**
* A test ended.
*/
public void endTest(Test test);
/**
* A test started.
*/
public void startTest(Test test);
}
//具体观察者角色,我们采用最简单的TextUI下的情况来说明(AWT,Swing对于整天做Web应用的人来说,已经很陌生了)
public class ResultPrinter implements TestListener {
//省略好多啊,主要是显示代码
……
//下面就是实现接口TestListener的四个方法
//填充方法的行为很简单的说
/**
* @see junit.framework.TestListener#addError(Test, Throwable)
*/
public void addError(Test test, Throwable t) {
getWriter().print("E");
}
/**
* @see junit.framework.TestListener#addFailure(Test, AssertionFailedError)
*/
public void addFailure(Test test, AssertionFailedError t) {
getWriter().print("F");
}
/**
* @see junit.framework.TestListener#endTest(Test)
*/
public void endTest(Test test) {
}
/**
* @see junit.framework.TestListener#startTest(Test)
*/
public void startTest(Test test) {
getWriter().print(".");
if (fColumn++ >= 40) {
getWriter().println();
fColumn= 0;
}
}
}
来看下我们的目标角色,随便说下,由于JUnit功能的简单,只有一个目标——TestResult,因此JUnit只有一个具体目标角色。
//好长的代码,好像没有重点。去掉了大部分与主题无关的信息
//下面只列出了当Failures发生时是怎么来通知观察者的
public class TestResult extends Object {
//这个是用来存放测试Failures的集合
protected Vector fFailures;
//这个就是用来存放注册进来的观察者的集合
protected Vector fListeners;
public TestResult() {
fFailures= new Vector();
fListeners= new Vector();
}
/**
* Adds a failure to the list of failures. The passed in exception
* caused the failure.
*/
public synchronized void addFailure(Test test, AssertionFailedError t) {
fFailures.addElement(new TestFailure(test, t));
//下面就是通知各个观察者的addFailure方法
for (Enumeration e= cloneListeners().elements(); e.hasMoreElements(); ) {
((TestListener)e.nextElement()).addFailure(test, t);
}
}
/**
* 注册一个观察者
*/
public synchronized void addListener(TestListener listener) {
fListeners.addElement(listener);
}
/**
* 删除一个观察者
*/
public synchronized void removeListener(TestListener listener) {
fListeners.removeElement(listener);
}
/**
* 返回一个观察者集合的拷贝,当然是为了防止对观察者集合的非法方式操作了
* 可以看到所有使用观察者集合的地方都通过它
*/
private synchronized Vector cloneListeners() {
return (Vector)fListeners.clone();
}
……
}
察者模式组成所需要的角色在这里已经全了。不过好像还是缺点什么……。呵呵,对!就是它们之间还没有真正的建立联系。在JUnit中是通过TestRunner来作的,而你在具体的系统中可以灵活掌握。
看一下TestRunner中的代码:
public class TestRunner extends BaseTestRunner {
private ResultPrinter fPrinter;
public TestResult doRun(Test suite, boolean wait) {
//就是在这里注册的
result.addListener(fPrinter);