被说了很多遍的设计模式---观察者模式

来源:互联网 发布:淘宝注册新账号 编辑:程序博客网 时间:2024/05/02 06:46

[把你的理性思维慢慢变成条件反射]

本文,我们讲介绍观察者模式,文章主题结构与上文一致。惯例,先来看看我们示例工程的环境:

操作系统:win7 x64

其他软件:eclipse mars,jdk7

-------------------------------------------------------------------------------------------------------------------------------------

经典问题:

EventListener,publisher/subscriber,MQ,Spring MVC等等。

思路分析:

要点一:拥有消息发布方,消息接收方两个角色。

要点二:其中一方功能严重依赖于另一方功能。

示例工程:


错误写法:

创建Publisher.java文件,具体内容如下:

package com.csdn.ingo.gof_Observer;import java.util.ArrayList;import java.util.List;public class Publisher {private List<Subsriber> observers = new ArrayList<Subsriber>();private String action;public void attach(Subsriber observer){observers.add(observer);}public String getAction() {return action;}public void setAction(String action) {this.action = action;}public void notifya(){for(Subsriber so:observers){so.update();}}}
创建Subscriber.java文件,具体内容如下:
package com.csdn.ingo.gof_Observer;public class Subsriber {private String name;private Publisher se;public Subsriber(String name, Publisher se) {super();this.name = name;this.se = se;}public void update() {System.out.println("name:"+name+",action:"+se.getAction());}}
创建Window.java文件,具体内容如下:

package com.csdn.ingo.gof_Observer;public class Window {public static void main(String[] args) {Publisher pub = new Publisher();Subsriber sub1 = new Subsriber("sub1",pub);Subsriber sub2 = new Subsriber("sub2",pub);pub.attach(sub1);pub.attach(sub2);pub.setAction("start");pub.notifya();}}

错误原因:

Publisher与Subscriber代码耦合度非常高。并且,新增Subscriber时,需要同时修改双方的内容。违反了“开闭原则”,“依赖倒转原则”,“迪米特法则”等。

错误写法(2):


创建Observer.java文件,具体内容如下:

package com.csdn.ingo.gof_Observer.one;public abstract class Observer {protected String name;protected Publisher sc;public Observer(String name, Publisher sc) {this.name = name;this.sc = sc;}public abstract void update();}
创建Publisher.java文件,具体内容如下:
package com.csdn.ingo.gof_Observer.one;import java.util.ArrayList;import java.util.List;public class Publisher {private List<Observer> observers = new ArrayList<Observer>();private String action;public void attach(Observer observer){observers.add(observer);}public void detach(Observer observer){observers.remove(observer);}public String getAction() {return action;}public void setAction(String action) {this.action = action;}public void notifya(){for(Observer so:observers){so.update();}}}
创建SubObserverA.java,SubObserverB.java文件,具体内容如下:

package com.csdn.ingo.gof_Observer.one;public class SubObserverA extends Observer {public SubObserverA(String name, Publisher sc) {super(name,sc);}public void update() {System.out.println("name:"+name+",action:"+sc.getAction()+",JOB-A");}}
创建Window.java文件,具体内容如下:
package com.csdn.ingo.gof_Observer.one;public class Window {public static void main(String[] args) {Publisher pub = new Publisher();SubObserverB subob1 = new SubObserverB("subob1",pub);SubObserverA subob2 = new SubObserverA("subob2",pub);pub.attach(subob1);pub.attach(subob2);pub.setAction("start");pub.notifya();}}<span style="font-family:Microsoft YaHei;font-size:14px;"></span>

错误原因:

在上一个版本的基础之上,我们将Observer角色进行抽象,在一定程度上满足了Observer角色的变化不会影响Publisher。但是,对于Publisher本身而言,缺少抽象。即,Observer本身也不需要关注具体的消息发布者是谁?在MQ异步消息模型中,经常需要使用的就是这种模型。可能存在多个消息发布者,而无论哪一个消息发布者发出的消息,接受方都需要进行接受并处理。因此,我们还需要进一步优化模型结构。

推荐写法:


创建Observer.java文件,具体内容如下:

package com.csdn.ingo.gof_Observer.two;public abstract class Observer {protected String name;protected Publisher sub;public Observer(String name, Publisher sub) {this.name = name;this.sub = sub;}public abstract void update();}
创建Publisher.java文件,具体内容如下:
public interface Publisher {void attach(Observer observer);void detach(Observer observer);void notifya();void setObserverState(String ObserverState);String getObserverState();}
创建ConcretePublisherA.java文件,具体内容如下:
package com.csdn.ingo.gof_Observer.two;import java.util.ArrayList;import java.util.List;public class ConcretePublisherA implements Publisher{private List<Observer> observers = new ArrayList<Observer>();private String action;public void attach(Observer observer) {observers.add(observer);}public void detach(Observer observer) {observers.remove(observer);}public void notifya() {for(Observer so:observers){so.update();}}public void setObserverState(String oc) {this.action = oc;}public String getObserverState() {// TODO Auto-generated method stubreturn action;}}
创建SubObserverA.java文件,具体内容如下:
package com.csdn.ingo.gof_Observer.two;public class SubObserverA extends Observer {public SubObserverA(String name, Publisher sub) {super(name,sub);}public void update() {System.out.println("name:"+name+",action:"+sub.getObserverState()+",JOB-A");}}
创建Window.java文件,具体内容如下:
public class Window {public static void main(String[] args) {Publisher wlw = new ConcretePublisherA();SubObserverB subob1 = new SubObserverB("s1", wlw);SubObserverA subob2 = new SubObserverA("n1", wlw);wlw.attach(subob2);wlw.attach(subob1);wlw.setObserverState("stop");wlw.notifya();}}

推荐原因:

这里仅作为功能原始,在实际使用时,会存在共享变量帮助实现消息发布方的抽象。另外,修改之后的代码为面向接口编程,符合“单一职责原则”等。

模式总结:

标准观察者模式UML结构图:


概念总结:

观察者模式:定义了一种一对多的依赖关系,让多个观察者对象同事监听某一个对象。这个主题对象在状态发生变化是,会通知所有观察者对象,使他们能够自动更新自己。

组成部分:Subject(抽象观察者合集),ConcreteSubject(具体实现类),Observer(抽象观察者),ConcreteObserver(具体观察者)四部分组成。

模板功能扩展:事件委托处理机制

委托,就是一种引用方法的类型。一旦委托分配了方法,委托将与该方法具有完全相同的行为。委托方法的使用可以向其他任何方法一样,具有参数和返回值。委托可以看做是对函数的抽象,是函数的“类”,委托的实例将代表一个具体的函数。一个委托可以搭载多个方法,所有方法被一次唤起。并且,可以是委托对象所搭载的方法并不需要属于同一个类。特别的,委托对象所搭载的所有方法必须具有相同的原型和形式,也就是拥有相同的参数列表和返回值类型


创建Event.java文件,具体内容如下:

package com.csdn.ingo.gof_Observer.three;import java.lang.reflect.Method;public class Event {// 要执行方法的对象private Object object;// 要执行的方法名称private String methodName;// 要执行方法的参数private Object[] params;// 要执行方法的参数类型private Class[] paramTypes;public Event() {}public Event(Object object, String methodName, Object... args) {this.object = object;this.methodName = methodName;this.params = args;contractParamTypes(this.params);}// 根据参数数组生成参数类型数组private void contractParamTypes(Object[] params) {this.paramTypes = new Class[params.length];for (int i = 0; i < params.length; i++) {this.paramTypes[i] = params[i].getClass();}}public void setParamTypes(Class[] paramTypes) {this.paramTypes = paramTypes;}// 执行该 对象的该方法public void invoke() throws Exception {Method method = object.getClass().getMethod(this.getMethodName(), this.getParamTypes());if (null == method) {return;}method.invoke(this.getObject(), this.getParams());}public String getMethodName() {return methodName;}public void setMethodName(String methodName) {this.methodName = methodName;}public Object[] getParams() {return params;}public void setParams(Object[] params) {this.params = params;}public Class[] getParamTypes() {return paramTypes;}public void setObject(Object object) {this.object = object;}public Object getObject() {return object;}}
创建EventHandler.java文件,具体内容如下;

package com.csdn.ingo.gof_Observer.three;import java.util.ArrayList;import java.util.List;public class EventHandler { //是用一个List       private List<Event> objects;              public EventHandler(){           objects=new ArrayList<Event>();       }       //添加某个对象要执行的事件,及需要的参数       public void addEvent(Object object,String methodName,Object...args){           objects.add(new Event(object,methodName,args));       }   //添加某个对象要执行的事件,及需要的参数       public void reEvent(Object object,String methodName,Object...args){           objects.add(new Event(object,methodName,args));       }     //通知所有的对象执行指定的事件       public void notifyX() throws Exception{           for(Event e : objects){               e.invoke();           }       }   }
创建ListenerA.java,ListenerB.java文件,具体内容如下:

package com.csdn.ingo.gof_Observer.three;import java.util.Date;public class ListenerA {public ListenerA(){System.out.println("ListenerA start listenering");}public void stop(Date date){System.out.println("ListenerA stop");}}
创建Notifier.java文件,具体内容如下:

package com.csdn.ingo.gof_Observer.three;public abstract class Notifier {private EventHandler eventHandler = new EventHandler();public EventHandler getEventHandler() {return eventHandler;}public void setEventHandler(EventHandler eventHandler) {this.eventHandler = eventHandler;}public abstract void attach(Object obj, String methodName, Object... args);public abstract void detach(Object obj, String methodName, Object... args);public abstract void notifya();}
创建NotifierA.java,NotifierB.java文件,具体内容如下:

package com.csdn.ingo.gof_Observer.three;public class NotifierA extends Notifier {@Overridepublic void attach(Object object, String methodName, Object... args) {// TODO Auto-generated method stubSystem.out.println("add someone");this.getEventHandler().addEvent(object, methodName, args);}@Overridepublic void detach(Object object, String methodName, Object... args) {System.out.println("re someone");this.getEventHandler().reEvent(object, methodName, args);}@Overridepublic void notifya() {System.out.println("notify all");try {this.getEventHandler().notifyX();} catch (Exception e) {e.printStackTrace();}}}
创建Window.java文件,具体内容如下:

package com.csdn.ingo.gof_Observer.three;import java.util.Date;public class Window {public static void main(String[] args) {Notifier n = new NotifierA();ListenerA la = new ListenerA();ListenerB lb = new ListenerB();n.attach(la, "stop", new Date());n.attach(lb, "stop", new Date());n.notifya();}}

反思:

应用场景:

  1. 一个模型中存在两个方面,其中一个方面依赖于另一个方便,并且被依赖方具有独立性。
  2. 被依赖方的数量具有不确定性,即,消息的接受方数量不限。
  3. 模型中的依赖层级具有不缺定性。即,可能存在多级依赖时,建议使用松耦合的方式进行关联。

优点:

  1. 实现表现层与数据逻辑层的分离,定义了稳定的消息发布机制,使得观察者的角色不受类型限制。
  2. Publisher与Subscriber双向解耦,互相不知道其内部细节。
  3. 支持广播通信,简化一对多的模型设计。
  4. 满足“开闭原则”“单一职责原则”“依赖倒转原则”等。

缺点:

  1. 在没有高性能组件的支持下,通过循环通知所有Subscriber,可能会产生一定延迟。
  2. 依赖层级对于上层不可见,因此,如果发生多级依赖,可能造成程序设计,测试的复杂度上升。
  3. 完全的松耦合模型,可能会造成两个开发人员团队理解上的困难。

-------------------------------------------------------------------------------------------------------------------------------------

至此,被说了很多遍的设计模式---观察者模式 结束


参考资料:

图书:《大话设计模式》

其他博文:http://blog.csdn.NET/lovelion/article/details/7563445


0 0
原创粉丝点击