本文共 10799 字,大约阅读时间需要 35 分钟。
观察者模式中,一个被观察者管理所有依赖它的观察者,并且在本身的状态改变时主动发出通知。这通常通过呼叫各观察者所提供的方法来实现。此种模式通常被用来实现事件处理系统。
角色 抽象被观察者角色:把所有对观察者对象的引用保存在一个集合中,每个被观察者角色都可以有任意数量的观察者。被观察者提供一个接口,可以增加和删除观察者角色。一般用一个抽象类和接口来实现。 抽象观察者角色:为所有具体的观察者定义一个接口,在得到主题的通知时更新自己。 具体被观察者角色:在被观察者内部状态改变时,给所有登记过的观察者发出通知。具体被观察者角色通常用一个子类实现。 具体观察者角色:该角色实现抽象观察者角色所要求的更新接口,以便使本身的状态与主题的状态相协调。通常用一个子类实现。如果需要,具体观察者角色可以保存一个指向具体主题角色的引用。 适用场景 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 Listlist = new ArrayList (); @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.Observableimport 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 Listobservers = new ArrayList (); 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);
转载地址:http://iuoql.baihongyu.com/