zoukankan      html  css  js  c++  java
  • 观察者模式与事件监听机制

    一、观察者模式

    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接口,需要注册在事件源上才能被调用。它监听事件,并进行事件处理或者转发。

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

    Untitled

    观察者(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

  • 相关阅读:
    JavaWeb
    申请百度开发者账号
    秋招C++面试相关总结索引
    游戏开发客户端
    Python源码剖析——02虚拟机
    Python源码剖析——01内建对象
    Pymongo 笔记
    调用其他文件__name__=='__main__'下代码
    Python 相关
    Python import本地模块
  • 原文地址:https://www.cnblogs.com/aoguren/p/4680692.html
Copyright © 2011-2022 走看看