观察者模式与事件监听机制

一、观察者模式

1.1 概述

有时被称作发布/订阅模式,观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态发生变化时,会通知所有观察者对象,使它们能够自动更新自己。它类似B/S架构模式,构建一个服务端,多个客户端显示。其实这个主题对象就像是一个信息源,当信息源的状态发送变化时,它会通知所有订阅者,使它们进行相应的处理。在百度百科中的例子是,用户界面可以作为一个观察者,业务数据是被观察者,用户界面观察业务数据的变化,发现数据变化后,就显示在界面上。

1.2 模式中的参与者

  • 抽象主题(Subject):它把所有观察者对象的引用保存到一个聚集里,每个主题都可以有任何数量的观察者。抽象主题提供一个接口,可以增加和删除观察者对象,以及通知所有观察者。

  • 具体主题(ConcreteSubject):将有关状态存入具体观察者对象;当具体主题内部状态放生改变时,通知所有注册过的观察者。
  • 抽象观察者(Observer):为所有的具体观察者定义一个接口,在得到主题通知时更新自己。
  • 具体观察者(ConcreteObserver):实现抽象观察者角色所要求的更新接口,以便使本身的状态与主题状态保持一致。

注意

观察者的信息是来源于主题中notify方法所传递信息,比如notify(String str)传递字符str信息,观察者update(String strObj)就会接收str信息,并进行相关操作。

1.3 适用性

1.当一个抽象模型有两个方面, 其中一个方面依赖于另一方面。将这二者封装在独立的对象中以使它们可以各自独立地改变和复用。

2.当对一个对象的改变需要同时改变其它对象, 而不知道具体有多少对象有待改变。

3.当一个对象必须通知其它对象,而它又不能假定其它对象是谁。换言之, 你不希望这些对象是紧密耦合的。

1.4 举例说明

公司开会,领导先通知各部门的负责人去会议室(注册、登记过程),在会议室中,大家都在等待领导的讲话(多个观察者关注同一个主题对象),领导开始说道,“今年的时间已过半了,可是业绩还没有完成预计的40%啊,大家可得努力”(通知过程),大家听到领导这番话时,市场部经理开始说话,“由于上半年的整个市场比较萎靡,因此市场拓展方面比较缓慢,但是经过上半年的调查,我们掌握了比较重要的信息,下半年,我们市场部肯定会拓展更多的份额”;研发部经理,“我们研发部会继续加班加点,保证完成任务”(各个观察者接到通知后的处理过程)。

ILeader:抽象主题(Subject)

public interface ILeader
{
	// 注册登记
	public void addManager(IManager manager);

	// 移除已注册的
	public void removeManager(IManager manager);

	// 通知所有经理
	public void notifyManagers(String str);
}

MyLeader:具体主题(ConcreteSubject)

public class MyLeader implements ILeader
{

	// 存储所有需注册登记的经理
	private List<IManager> managerList = new ArrayList<IManager>();

	@Override
	public void addManager(IManager manager)
	{
		synchronized (this)
		{
			if (manager != null && !(managerList.contains(manager)))
			{
				managerList.add(manager);
			}
		}
	}

	@Override
	public void removeManager(IManager manager)
	{
		synchronized (this)
		{
			if (managerList.contains(manager))
			{
				managerList.remove(manager);
			}
		}
	}

	@Override
	public void notifyManagers(String str)
	{
		for (IManager iManager : managerList)
		{
			iManager.update(str);
		}
	}
}

IManager:抽象观察者(Observer)


public interface IManager
{

	/**
	 * 更新
	 * @param str 与ILeader的通知参数一致
	 */
	public void update(String str);
}

MarketingManager:具体观察者(ConcreteObserver)

public class MarketingManager implements IManager
{

	@Override
	public void update(String str)
	{
		System.out.print("市场部接收到命令: ");
		doSomething(str);
	}

	private void doSomething(String str)
	{
		if (str.equals("努力"))
		{
			System.out.println("下半年,我们市场部肯定会拓展更多的份额");
		}
	}
}

DevelopManager:具体观察者(ConcreteObserver)

public class DevelopManager implements IManager
{

    @Override
    public void update(String str)
    {
        System.out.print("研发部接收到命令: ");
        doSomething(str);
    }

    private void doSomething(String str)
    {
        System.out.println("我们研发部会继续加班加点,保证完成任务");
    }
}

测试类:

public class TestMain
{
	public static void main(String[] args)
	{
		ILeader leader = new MyLeader();
		IManager marketManager = new MarketingManager();	// 市场部
		IManager developManager = new DevelopManager();		// 研发部
		// 注册,登记过程
			// 方式一
		leader.addManager(marketManager);
		leader.addManager(developManager);
			// 方式二 匿名:以工程部为例
		leader.addManager(new IManager()					// 工程部
		{

			@Override
			public void update(String str)
			{
				System.out.print("工程部接收到命令: ");
				if (str.equals("努力"))
				{
					doSomething();
				}
			}
			private void doSomething()
			{
				System.out.println("我们工程部会加快工程的实施进度");
			}
		});
		System.out.println("领导讲话:先谈谈上半年的业绩!");
		// 通知过程
		System.out.println("发送努力命令");
		leader.notifyManagers("努力");
	}
}

输出结果:

领导讲话:先谈谈上半年的业绩!
发送努力命令
市场部接收到命令: 下半年,我们市场部肯定会拓展更多的份额
研发部接收到命令: 我们研发部会继续加班加点,保证完成任务
工程部接收到命令: 我们工程部会加快工程的实施进度

在实际中,为了方便性,我们更多使用的是第二种注册方式。

1.5 我推你拉

观察者模式在关于目标(主题)角色、观察者角色通信的具体实现中,有两个版本。

1) 拉模式:目标角色在发生变化后,仅仅告诉观察者角色“我变化了”;观察者角色如果想要知道具体的变化细节,则就要自己从目标角色的接口中得到。拉模式是想要就主动表白获取。

2) 推模式:通知你发生变化的同时,通过一个参数将变化的细节传递到观察者角色中去。推模式是管你要不要,先给你啦。

这两种模式的使用,取决于系统设计时的需要。如果目标角色比较复杂,并且观察者角色进行更新时必须得到一些具体变化的信息,则“推模式”比较合适。如果目标角色比较简单,则“拉模式”就很合适啦。

二、事件监听机制

当事件源对象上发生操作时,它将会调用事件监听器的一个方法,并在调用该方法时传递事件对象过去。

2.1 组成结构

Java中的事件监听机制主要由事件源、事件对象、事件监听器三个部分组成。

1)事件源(event source):

具体的事件源,比如说,你点击一个button,那么button就是event source,要想使button对某些事件进行响应,你就需要注册特定的listener。

2)事件对象(event object):

一般继承自java.util.EventObject类,封装了事件源对象以及与事件相关的信息。它是在事件源和事件监听器之间传递信息的。

3)事件监听器(event listener):

实现java.util.EventListener接口,需要注册在事件源上才能被调用。它监听事件,并进行事件处理或者转发。

然而,事件监听机制与观察者模式的关系呢?

观察者(Observer)相当于事件监听者,被观察者(Observable)或者说主题(Subject)相当于事件源和事件,执行逻辑时通知observer即可触发oberver的update,同时可传被观察者和参数。

其实事件机制中的“事件对象”就相当于上例观察者模式中的notify中的String参数对象。

2.2 举例说明

DoorEvent:事件对象(event object)

public class DoorEvent extends EventObject
{
	private String doorState = "";	// 表示门的状态,有“开”和“关”两种
	public DoorEvent(Object source)
	{
		super(source);
	}

	public void setDoorState(String doorState)
	{
		this.doorState = doorState;
	}

	public String getDoorState()
	{
		return this.doorState;
	}
}

IDoorListener:事件监听器(event listener)

public interface IDoorListener extends EventListener
{
	//EventListener是所有事件侦听器接口必须扩展的标记接口、因为它是无内容的标记接口、
    //所以事件处理方法由我们自己声明如下:
	public void dealDoorEvent(DoorEvent event);
}

FrontDoorListener:事件监听器(event listener)

public class FrontDoorListener implements IDoorListener
{
	/**
	 * 做具体的开门,关门动作
	 * @param event
	 */
	@Override
	public void dealDoorEvent(DoorEvent event)
	{
		if (event.getDoorState()!=null && event.getDoorState().equals("open"))
		{
			System.out.println("前门打开");
		}
		else
		{
			System.out.println("前门关闭");
		}
	}
}

DoorManager:事件源(event source)

public class DoorManager
{
	private List<IDoorListener> listeners = new ArrayList();

	public void addDoorListener(IDoorListener listener)
	{
		synchronized (this)
		{
			if (listener != null && !(listeners.contains(listener)))
			{
				listeners.add(listener);
			}
		}
	}

	public void removeDoorListener(IDoorListener listener)
	{
		synchronized (this)
		{
			if (listeners.contains(listener))
			{
				listeners.remove(listener);
			}
		}
	}

	public void notifyDoors(DoorEvent event)
	{
		for (IDoorListener iDoorListener : listeners)
		{
			iDoorListener.dealDoorEvent(event);
		}
	}

	/**
	 * 模拟开门事件
	 */
	public void fireOpend()
	{
		if (listeners == null)
		{
			return;
		}
		DoorEvent event = new DoorEvent(this);
		event.setDoorState("open");

		notifyDoors(event);
	}
}

测试类:

public class TestMain
{

	public static void main(String[] args)
	{
		DoorManager doorManager = new DoorManager();

		// 添加监听器
		doorManager.addDoorListener(new FrontDoorListener());
		doorManager.addDoorListener(new IDoorListener()
		{
			@Override
			public void dealDoorEvent(DoorEvent event)
			{
				if (event.getDoorState() != null && event.getDoorState().equals("open"))
				{
					System.out.println("后门打开,警示灯亮起");
				}
				else
				{
					System.out.println("后门关闭,警示灯熄灭");
				}
			}
		});

		// 模拟事件
		System.out.println("模拟门打开事件");
		doorManager.fireOpend();

		System.out.println("模拟门关闭事件");
		DoorEvent doorEvent = new DoorEvent(doorManager);
		doorEvent.setDoorState("close");
		doorManager.notifyDoors(doorEvent);
	}
}

输出结果:

模拟门打开事件
前门打开
后门打开,警示灯亮起
模拟门关闭事件
前门关闭
后门关闭,警示灯熄灭

另一示例:

Button 按钮事件监听-又说观察者模式

参考:

1、http://www.cnblogs.com/wangjq/archive/2012/07/12/2587966.html

2、http://www.cnblogs.com/abcdwxc/archive/2007/09/19/898856.html

3、http://blog.csdn.net/ai92/article/details/375691

4、http://enjiex.iteye.com/blog/1067650

5、http://ericliu1986.iteye.com/blog/629562

6、http://blog.csdn.net/xiaolang85/article/details/5316859

时间: 2024-12-09 23:38:06

观察者模式与事件监听机制的相关文章

Spring架构揭秘-事件监听机制

一.事件监听机制概述 二.事件监听机制结构 三.Spring监听机制架构 Spring的Application拥有发布事件并且注册事件监听器的能力,拥有一套完整的事件发布和监听机制.在Java中,通过java.util. EventObject来描述事件,通过java.util. EventListener来描述事件监听器,在众多的框架和组件中,建立一套事件机制通常是基于这两个接口来进行扩展. 在一个事件体系中,有以下几个重要的概念. 1.事件源:事件对象的产生者,任何一个EventObject

Java事件监听机制与观察者设计模式

一. Java事件监听机制 1. 事件监听三要素: 事件源,事件对象,事件监听器 2. 三要素之间的关系:事件源注册事件监听器后,当事件源上发生某个动作时,事件源就会调用事件监听的一个方法,并将事件对象传递进去,开发者可以利用事件对象操作事件源. 3. 入门程序 1 /** 2 * Created by IntelliJ IDEA. 3 * 4 * @Auther: ShaoHsiung 5 * @Date: 2018/8/29 21:25 6 * @Title: 事件监听入门程序 7 * @D

观察者模式和事件监听模式的区别

事件监听模式更像是观察者模式的进阶. 观察者模式中,'主题'会在特定逻辑下通知所有'观察者'.如果这个通知不包含任何信息,那么这种实现就是通常的观察者模式. 如果'主题'通知'观察者'的过程带有一些<其他信息>.那么'主题'本身已经上升成为了'事件源',而通知中带有的<其他信息>经过封装就成为了事件. 事件监听模式的优势:在很多应用场景中,通知中附带的<其他信息>是必不可少的,事件Event则对这些<信息>进行了封装,使它本身拥有了多态的特性.每个事件对象就

Java中的事件监听机制

鼠标事件监听机制的三个方面: 1.事件源对象: 事件源对象就是能够产生动作的对象.在Java语言中所有的容器组件和元素组件都是事件监听中的事件源对象.Java中根据事件的动作来区分不同的事件源对象,动作发生在哪个组件上,那么该组件就是事件源对象 2.事件监听方法: addMouseListener(MouseListener ml) ;该方法主要用来捕获鼠标的释放,按下,点击,进入和离开的动作:捕获到相应的动作后,交由事件处理类(实现MouseListener接口)进行处理. addAction

.NET事件监听机制的局限与扩展

.NET中把“事件”看作一个基本的编程概念,并提供了非常优美的语法支持,对比如下C#和Java代码可以看出两种语言设计思想之间的差异. // C#someButton.Click += OnSomeButtonClick; // JavasomeButton.addActionListener( new ActionListener(){ public void actionPerformed(){ ... } }); 在我们的软件中就大量使用事件来对监听者与发布者解耦,但也遇到了一些局限,在这

4.JAVA之GUI编程事件监听机制

事件监听机制的特点: 1.事件源 2.事件 3.监听器 4.事件处理 事件源:就是awt包或者swing包中的那些图形用户界面组件.(如:按钮) 事件:每一个事件源都有自己特点有的对应事件和共性事件.(如:鼠标单击事件) 监听器:将可以触发某一事件的动作(不止一个动作)都已经封装到了监听器中. 以上三者,在java中都已经定义好了,直接获取其对象来用就可以了. 我们要做的事情是,就是对产生的动作进行处理. 图解事件监听机制: 4.JAVA之GUI编程事件监听机制,布布扣,bubuko.com

GUI编程笔记05:GUI事件监听机制原理和举例说明

1.事件监听机制:       A:事件源          事件发生的地方       B:事件             就是要发生的事情       C:事件处理       就是针对发生的事情做出的处理方案       D:事件监听       就是把事件源和事件关联起来 2.举例:人受伤事件. 事件源:人(具体的对象)         Person p1 = new Person("张三");         Person p2 = new Person("李四&qu

关于事件监听机制的总结(Listener和Adapter)

记得以前看过事件监听机制背后也是有一种设计模式的.(设计模式的名字记不清了,只记得背后实现的数据结构是数组.) 附上事件监听机制的分析图: 一个事件源可以承载多个事件(只要这个事件源支持这个事件就可以,男人就不支持生孩子的事件)  事件和监听器有对应关系的. 下面用awt中的标准图形化界面分析: 接口WindowListener 中有一个实现类WindowAdapter类.适配器类. EventListener.java 下面是所有超级接口EventListener.java的源代码   其实就

Java 中的事件监听机制

看项目代码时遇到了好多事件监听机制相关的代码.现学习一下: java事件机制包含三个部分:事件.事件监听器.事件源. 1.事件:继承自java.util.EventObject类,开发人员自己定义. package com.shuyan.javatest.EventTest; import java.util.EventObject; public class DnsEvent extends EventObject { private String userData; private long