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

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

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

操作系统: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 stub
		return 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 {
	
	@Override
	public void attach(Object object, String methodName, Object... args) {
		// TODO Auto-generated method stub
		System.out.println("add someone");
		this.getEventHandler().addEvent(object, methodName, args);
	}

	@Override
	public void detach(Object object, String methodName, Object... args) {
		System.out.println("re someone");
		this.getEventHandler().reEvent(object, methodName, args);
	}

	@Override
	public 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
    收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
©️2022 CSDN 皮肤主题:编程工作室 设计师:CSDN官方博客 返回首页
评论

打赏作者

y-yg

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值