`
songlixiao
  • 浏览: 23124 次
  • 性别: Icon_minigender_1
  • 来自: 青岛
社区版块
存档分类
最新评论

社区性质网站事件驱动机制-观察者模式实用

    博客分类:
  • java
阅读更多

    OECP社区用户积分与动态部分是使用基于观察者模式的思路实现的。观察者模式的介绍网上有好多,在这就只简单提一下,一带而过吧(如果以后有时间和必要再写一篇初级的让初学的朋友们看的)。这里主要介绍一下我们社区中利用这种模式的一个实现的思路,就作为观察者模式的一个实战范例吧.
    下面简单说下观察者模式是个什么。所谓“观察者”,就是观众,围观群众。但只有围观群众还构不成观察者模式,还需要有“被观察者”。观察者模式由“被观察者”和“观察者”共同构成。一个“被观察者”可能会有0到n个“观察者”。
    在我们社区中“被观察者”可以是“博客”、“用户”、“组件设计”、“讨论”、“留言”等等。“观察者”则是“积分”、“动态”、“邮件”、“站内信”、“短信”等等。举个例子:当用户发表一篇博客,各个关注者将会被通知,积分系统得知后会给用户做加分,动态系统会增加动态信息,其他系统也会根据自己的需要做一些处理。
    其实观察者模式与java中的事件机制是非常相似的。当事件发生时,所有的监听器将会得到事件的信息,做自己分内的工作。所以“被观察者”我们可以认为是事件源EventSource,“观察者”就是监听器Listener。一个事件源可以有多个动作叫做Action,事件源发起动作称之为事件Event。
    当事件源EventSource发生一个动作Action时,产生一个事件Event。所有的关注者Listener将的到这个Event,并根据Event中的信息,来完成自己要做的事情。这就是整个事件机制也就是观察者模式的大体过程。
    在实际的程序中,我们可能会有几个点需要解决一下。一个是,动作发生时,如何产生一个事件?另一个是,监听器如何关注事件并被通知到?对于第一个问题,我们只需要一个点火装置在动作发生时fire一下。解决第二个问题,我们需要一个可以容纳足够“观察者”的容器,就好像观看演出的剧场观众席。当事件发生时,依次通知这些“观察者”。
    那么整体的结构应该是这样的:

    通常情况下,监听机制有这些东西就可以建立了。具体的监听器,只要实现Listener接口,然后添加到事件源中就可以对事件源所发生的事件进行观察了。不过我觉得可能这样看起来不是很舒服,因为写程序时,我们应该尽可能让业务服务中少一些不相干的东西。比如上面EventSource里的listeners和一些fireEvent这样的方法。所以改造一下成下面这种形式,可以让程序看起来干净一些。

    另外获得一个好处:经过这一步改造后,事件源与“事件类”、“监听器类”隔绝了,我们在系统中使用这种机制变得更加方便了。
    在实际的社区应用中事件机制就是基于这种关系组织建立起来的。为了更加便于开发和扩展,在应用时又做了一些扩充。
    由于社区中需要关注的事件并不是很多,一个事件管理器已经足够使用,我们可以采用使用同一套管理器管理多类事件并分发给相关监听者的方式。
    采用这种方式,首先要区分出事件的来源是什么,以便发生事件时通知相关的监听器。因此,增加了一个事件来源标识的枚举,为了区分事件来源,是博客、微博、讨论还是其他什么。

 

public enum EnumEventSourceSign {  
    @EnumDescription("博客")  
    blog,  
    @EnumDescription("微博")  
    micBlog,  
    @EnumDescription("组件")  
    bc,  
    @EnumDescription("组件设计")  
    bcWiki,  
    @EnumDescription("组件讨论")  
    bcTopic,  
    @EnumDescription("其他")  
    others,  
    @EnumDescription("留言")  
    leaveWord,  
    @EnumDescription("活动")  
    activity,  
    @EnumDescription("项目管理")  
    prj,  
    @EnumDescription("资讯")  
    news  
}  

     监听器也可以使用统一接口:public interface ActionListener {

  
    public void onAction(ActionEvent event);  
      
    /** 
     * 得到原标志 
     * @author slx 
     * @date 2010-7-9 上午10:04:35 
     * @modifyNote 
     * @return 
     */  
    EnumEventSourceSign getSourceSign();  
}  

 封装一个事件对象,把监听器处理事件时可能需要的内容放到里面

 

/** 
 * 事件 
 *  
 * @author slx 
 * @date 2010-7-7 下午05:27:37 
 * @version 1.0 
 */  
public class ActionEvent<T> {  
  
    /** 当前用户 **/  
    private User curUser;  
  
    /** 事件相关的对象 **/  
    private T source;  
  
    private String action;  
  
    public ActionEvent(User curUser, T source, String action) {  
        this.curUser = curUser;  
        this.source = source;  
        this.action = action;  
    }  
  
    public User getCurUser() {  
        return curUser;  
    }  
  
    public T getSource() {  
        return source;  
    }  
  
    public String getAction() {  
        return action;  
    }  
  
}  

 还需要一个事件管理器,来触发事件。

接口:

 

/** 
 * 事件触发器接口 
 * @author slx 
 * @date 2010-7-7 下午05:31:59 
 * @version 1.0 
 */  
public interface EventHandler {  
   
    /** 
     * 发起事件 
     * @author slx 
     * @date 2010-7-7 下午05:31:59 
     * @modifyNote 
     * @param curUser 
     *      User 当前用户 
     * @param source 
     *      Object 事件源 
     * @param sourceSign 
     *      EnumEventSourceSign 来源标志 
     * @param action 
     *      String 事件标示 
     */  
    public void fireEvent(User curUser ,Object source ,EnumEventSourceSign sourceSign, String action);  
      
}  

 实现:

 

import java.util.ArrayList;  
import java.util.LinkedHashMap;  
import java.util.List;  
  
import com.posoft.user.eo.User;  
  
/** 
 * 事件触发器 
 * @author slx 
 * @date 2010-7-7 下午04:50:31 
 * @version 1.0 
 */  
public class EventHandlerImpl implements EventHandler {  
      
    private List<ActionListener> listeners;  
      
    private LinkedHashMap<EnumEventSourceSign, List<ActionListener>> m_listeners;  
      
    public void fireEvent(User curUser ,Object source ,EnumEventSourceSign sourceSign, String action){  
        List<ActionListener> l = m_listeners.get(sourceSign);  
        if(l!=null){  
            ActionEvent event = new ActionEvent(curUser,source,action);  
            for (ActionListener actionListener : l) {  
                actionListener.onAction(event);  
            }  
        }  
    }  
  
    public void init(){  
        m_listeners = new LinkedHashMap<EnumEventSourceSign, List<ActionListener>>();  
        if(listeners!=null){  
            for (ActionListener al : listeners) {  
                List<ActionListener> ls = m_listeners.get(al.getSourceSign());  
                if(ls == null){  
                    ls = new ArrayList<ActionListener>();  
                    m_listeners.put(al.getSourceSign(), ls);  
                }  
                ls.add(al);  
            }  
        }  
    }  
  
    public void setListeners(List<ActionListener> listeners) {  
        this.listeners = listeners;  
    }  
      
}

 这里删除掉了添加监听器的add方法,因为使用了Spring的注入机制。所有监听器直接用Spring注入到了事件管理器中。

 

<bean id="eventHandler" class="com.posoft.event.EventHandlerImpl"  
        init-method="init">  
        <property name="listeners">  
            <!-- 请将需要的监听器添加到里面 监听器必须继承自 com.posoft.event.baselistener包中的类 -->  
            <list>  
                <!-- 动态、通知监听开始 -->  
                <ref bean="BCWikiDynamicListener" />  
                <ref bean="BCTopicDynamicListener" />  
                .......  
                <!-- 动态、通知监听结束-->  
                <!-- 积分监听 开始 -->  
                <ref bean="bcPointsListener" />  
                <ref bean="bcTopicPointsListener" />  
                <ref bean="bcWikiPointsListener" />  
                .......  
                <!-- 积分监听 结束 -->  
            </list>  
        </property>  
    </bean>  

 这套事件机制基本的接口、类和配置有这些基本就可使用了。但是,为了代码写起来更简单更优雅,还可以做一些抽象和封装。

首先,目前的监听器现在只有一个onAction方法,显然不够用,虽然都是对一个事件源的监听,但是不同的动作,会做不同的处理,虽然也可以都用一个onAction方法自行在内部区分动作来处理,但代码会很乱。因此,增加一个监听器的抽象类实现监听器接口,这个类负责区分动作,转向相应的方法(转向的规则是:转到与事件发生的动作名相同的监听器方法中)。实际的监听器,继承自这个类。

 

/** 
 * 事件监听父类 
 * @author slx 
 * @date 2010-7-9 上午09:51:24 
 * @version 1.0 
 */  
public abstract class BaseEventListener  implements ActionListener {  
  
    @Override  
    public void onAction(ActionEvent event) {  
        String action = event.getAction().toLowerCase();  
        try {  
            Class[] params = new Class[]{event.getClass()};  
            Method md = this.getClass().getDeclaredMethod(action, params);  
            md.invoke(this, new Object[]{event});  
        } catch (NoSuchMethodException e) {  
            System.err.println("★★★★★★ 监听器未定义事件对应的处理方法: 监听器[" + this.getClass().getName()+ "] 监听动作 → " + action);  
        } catch (Exception e) {  
            throw new RuntimeException(e);  
        }   
    }  
}

 使用这个类作为监听器的父类,需要遵守处理方法与需要监听的动作名称相同的规则。如果需要特别的规则,子类重写onAction方法即可。

这样子一共有了两个规则:

  1. 事件管理器通过枚举EnumEventSourceSign来区分事件的来源。监听器通过约定接口返回EnumEventSourceSign来标示自己身份。
  2. 监听器通过事件动作action的名称,来路由到相应的处理方法。

 

到了这一步,实现监听器的开发人员就可以根据约定来实现监听器了。不过这样还是不太舒服,我们根据不同的事件源定义一套监听器父类,定义好抽象的处理方法,让实际的实现人员集成过来填空岂不是更爽。例如这样:

 

/** 
 * 博客相关事件监听 
 * @author slx 
 * @date 2010-7-8 上午10:39:51 
 * @version 1.0 
 */  
public abstract class BlogBaseEventListener extends BaseEventListener {  
      
    @Override  
    public final EnumEventSourceSign getSourceSign() {  
        return EnumEventSourceSign.blog;  
    }  
  
    /** 
     * 发表博客 
     * @author slx 
     * @date 2010-7-9 上午09:45:00 
     * @modifyNote 
     * @param e 
     */  
    public abstract void addblog(ActionEvent<Blog> e);  
      
    /** 
     * 删除博客 
     * @author slx 
     * @date 2010-7-9 上午09:45:25 
     * @modifyNote 
     * @param e 
     */  
    public abstract void delblog(ActionEvent<Blog> e);  
      
    /** 
     * 从业务组件中删除博客 
     * @author slx 
     * @date 2010-7-8 下午04:49:05 
     * @modifyNote 
     * @param e 
     *  ActionEvent<List> list(0)博客,list(1)业务组件 
     */  
    public abstract void delblogfrombc(ActionEvent<List> e);  
      
    /** 
     * 评论博客 
     * @author slx 
     * @date 2010-7-8 下午04:49:05 
     * @modifyNote 
     * @param e 
     *  ActionEvent<List> list(0)评论,list(1)博客 
     */  
    public abstract void comment(ActionEvent<List> e);  
      
    /** 
     * 删除博客评论 
     * @author slx 
     * @date 2010-7-8 下午04:49:05 
     * @modifyNote 
     * @param e 
     *  ActionEvent<List> list(0)评论,list(1)博客 
     */  
    public abstract void delcomment(ActionEvent<List> e);  
      
    /** 
     * 博客投票 
     * @author slx 
     * @date 2010-10-27 下午03:01:13 
     * @modifyNote 
     * @param e 
     */  
    public abstract void vote(ActionEvent<BlogVote> e);  
}  

 如此一来,开发监听器的只需要集成相应的监听器父类,然后将自己的实现配置到Spring的配置文件中让它被注入到事件管理器中就可以了。

总体结构如下图:

最后总结一下工作步骤:

  1. 服务类使用事件管理器EventHandler的fire方法发起事件,发起事件需要传入几个参数:当前用户、事件源、来源标识、动作名称。
  2. 有了上面传入的参数,事件管理器就可以根据这些参数创建事件对象ActionEvent了,然后根据来源标识,调用注册到管理器中的相关Listener的onAction方法。
  3. Listener的onAction方法中,从Event中取得事件动作名,调用自身与动作名同名的方法完成自己分内的业务。

在这套机制中,事件管理器,使用同一个,所有事件源的监听都注册到这个管理器中。这种模式适合事件比较少的系统中,如果事件比较多,还是建议拆分成多套类似的机制来处理,以免服务器内存吃不消。
此博客是作者本人从 OECP社区转载过来的,大家对这个设计有什么好的意见和看法可以到 OECP社区和我讨论哦。原文连接:http://www.oecp.cn/hi/slx/blog/2206

 

提供该文档的机构为 OECP社区,更多的博客文章可以到 OECP社区查看。该文档附件欢迎各位转载,但是在没有获得文章作者许可之前,不得对文章内容或者版权信息进行更改,版权归OECP社区所有,仅此声明。

2
8
分享到:
评论

相关推荐

    设计模式系列博客--观察者模式

    观察者模式(Observer Pattern)是设计...总的来说,观察者模式和策略模式是软件设计中非常实用的工具,它们有助于我们构建松耦合、易于维护的系统。在实际开发中,合理地运用这些模式能大大提高代码的质量和可维护性。

    委托事件实例程序-观察者模式

    在编程领域,委托和事件是C#语言中的...总的来说,委托和事件是C#中的重要特性,它们与观察者模式相结合,为我们提供了强大的事件驱动编程能力。理解并熟练运用这些概念,能够帮助开发者构建更加灵活和健壮的应用程序。

    游戏设计2-观察者模式的应用-攻击

    4. **可扩展性**:通过观察者模式,可以轻松地添加新的攻击类型或响应机制。只需要创建新的观察者类来处理特定的攻击事件,而不需要修改已有代码。 5. **性能优化**:为了提高性能,观察者模式还可以结合“发布-...

    HeadFirst 设计模式学习笔记2--观察者模式 demo

    在实际开发中,观察者模式广泛应用于事件驱动编程、GUI组件通信、消息队列等场景。例如,在Web开发中,可以使用观察者模式处理用户交互事件,当用户触发某个操作时,相关的监听器(观察者)会得到通知并执行相应的...

    事件驱动风格+观察者模式java

    在编程领域,事件驱动风格和观察者模式是两种常见的设计模式,它们在Java开发中有着广泛的应用。这两种模式主要用于处理异步数据处理和组件之间的通信,使得程序更加灵活且易于维护。 事件驱动风格是一种编程范式,...

    test2012-seven-2-观察者模式

    观察者模式,也被称为发布-订阅(Publish-Subscribe)模式,是软件设计模式中的一个经典概念,主要用于在对象之间建立一种一对多的依赖关系。当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知并自动更新...

    设计模式-观察者模式

    观察者模式(Observer Pattern)是软件设计模式中的一种行为模式,它定义了对象之间的一对多依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知并自动更新。这种模式在iOS开发中尤其常见,因为...

    设计模式实现——观察者模式

    两者的主要区别在于观察者模式更强调对象间的依赖关系,而发布-订阅模式则侧重于事件驱动。 在实际开发中,我们可以结合其他设计模式,如工厂模式、策略模式等,来优化观察者模式的实现,提高代码的可读性和可维护...

    设计模式之观察者模式

    - 事件驱动:观察者模式天然适合事件驱动编程,可以实现事件的发布和订阅。 然而,观察者模式也有一些潜在的问题需要注意: - 如果观察者数量过多,通知过程可能会消耗大量资源。 - 如果主题没有有效地管理观察者,...

    观察者模式练习

    观察者模式(Observer Pattern)是软件设计模式中的一种行为模式,它定义了对象之间的一对多依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知并自动更新。这种模式常用于事件驱动的系统或者...

    设计模式--观察者 c++版本

    总的来说,观察者模式在C++中提供了事件驱动和发布/订阅机制的基础,是构建响应式系统的关键。它在诸如用户界面更新、事件处理、数据绑定等场景中都有广泛的应用。理解和熟练掌握这种设计模式对于提升软件设计水平和...

    Java内置观察者模式

    观察者模式广泛应用于GUI编程、事件驱动系统、订阅-发布系统等。例如,在GUI中,按钮点击事件可以视为一个主题,多个组件可以作为观察者监听这个事件。当按钮被点击时,所有注册的组件都会接收到通知并做出响应。 #...

    56丨观察者模式(上):详解各种应用场景下观察者模式的不同实现方式1

    * 事件驱动编程:在事件驱动编程中,观察者模式可以用来处理事件的发布和订阅。 * 消息队列:在消息队列中,观察者模式可以用来处理消息的发布和订阅。 * 订阅发布系统:在订阅发布系统中,观察者模式可以用来处理...

    设计模式之观察者模式(Observer)

    主题是被观察的对象,它负责存储观察者列表,并提供添加、删除观察者的方法以及通知观察者状态变化的机制。观察者是关注主题状态的对象,当主题状态改变时,它们会收到通知并执行相应的操作。 在实现观察者模式时,...

    探索Ruby中的观察者模式:事件驱动编程的艺术

    ### 探索 Ruby 中的观察者模式:事件驱动编程的艺术 **一、Ruby 语言概述** Ruby 是一种高级的、面向对象的编程语言,由日本的开发者松本行弘(Yukihiro "Matz" Matsumoto)在 1995 年创建。其设计目标是简单、...

    观察者模式简单例子

    这个例子很好地展示了观察者模式的灵活性和实用性,允许主题对象(天气数据)与多个观察者(显示设备)解耦,只要主题的状态发生变化,所有相关的观察者都能及时得到通知并做出反应。这种模式广泛应用于事件驱动编程...

    java23种设计模式之观察者模式

    观察者模式常用于事件驱动编程、GUI组件通信、消息发布/订阅系统等。例如,在Java Swing和JavaFX中,组件之间的交互就大量使用了观察者模式。 ### 5. 扩展与变体 观察者模式有多种变体,如发布/订阅模式、模型-视图...

    观察者模式学习

    观察者模式(Observer Pattern)是一种行为设计模式,它允许你定义一个订阅机制,可以在对象状态改变时通知多个“观察”该对象的其他对象。在软件工程中,这种模式被广泛应用于事件驱动编程和异步通信中。在这个主题...

    观察者模式Demo

    例如,Java的`java.util.Observable`和`java.util.Observer`类就是观察者模式的一个实现,用于事件驱动编程。另外,许多现代框架,如Spring,也支持观察者模式,使得开发者能够轻松地实现事件监听和响应。

    观察者模式(附代码和解释)下载

    观察者模式是一种非常实用的设计模式,它可以帮助我们构建响应式和事件驱动的应用程序。通过定义明确的接口和角色,我们可以轻松地扩展系统的功能,同时也使得各个组件之间的耦合度更低,更易于维护。此外,观察者...

Global site tag (gtag.js) - Google Analytics