“Event/Listener”是我们常用的一种设计模式,其中最常见的莫过于控件事件了。写过 Swing 程序的同学应该都非常了解,譬如,我们可以调用JButton的addActionListener方法,这样的话,当 JButton被点击时,会自动回调你的listener方法。
但正是由于控件事件被用得非常广泛,以致于很多同学把事件当成是控件的一种专有属性,而忽略了“事件” 其实是一种典型的Observer模式,它原本可以用来解决更多的架构设计问题。本文,我们来介绍 AOM 2.0中的模型事件。 请注意,此处所说的事件,并不是指 AOM 中的组件事件,确切的说,这是一种后台的“模型事件”,并且,事件的发送与接收,AOM都为用户做了非常好的封装。
如果我们把事件模型分为两部分,演讲者(即事件起源)与听众(即Listener);那么,从程序的思维来看: 事件模型的显著特点是:一个演讲者可能会有多个听众,而听众可以任意的增加或删除。 因此,事件经常用来进行解耦。譬如说某一个业务数据发生变化,这种变化可能会影响到界面或者其它数据的变化, 但事先,我们并不知道会产生多少变化,于是我们就把“业务数据发生变化”打包成一个事件,谁对此事件感兴趣, 谁就注册并侦听此事件。
但同时,我们发觉,针对常规的事件实现方式,如果你要增加一个 Listener,首先必须要获得“事件发生处”的句柄,然后再通过此句柄显式的增加Listener,写成伪码就是: speaker.addListener(myListener)。无疑,这种实现方式存在一个明显的弊端,那就是必须要事先获得“事件发生地”的句柄, 这就意味着如果你要扩展程序功能,那么,你需要对旧有代码进行更改,而且,这种更改的成本还比较大。有没有更好的实现机制呢?
AOM 2.0中,提出了“Message Bus”的概念,即“消息总线”。发送消息是向“Message Bus”中发送,并且需要注明发送的消息类型;同时,任何人都可以随时从“Message Bus”中接听消息,并且可以通过消息类型来进行筛选,只接收你感兴趣的话题。你可以把“Message Bus”想象成全国的电视网络,电视台就是消息发送者,并且,不同的电视台播放不同类型的节目; 而如果你希望收看这些节目,那么,你只需要买台电视机,选择频道,即可收看到你感兴趣的节目。
AOM 2.0的消息总线是如何使用的呢?我们假设这样一种场景:某公司的一位程序员进行费用申请,他申请的费用会经过所属项目组的项目经理的审批。 如果转换成消息模型那就是:“申请费用”是一个事件,而“项目经理“对此感兴趣。
为了让我们的视线关注于事件本身,程序代码我们尽可能的精简:
<f:view xmlns="http://www.w3.org/1999/xhtml" xmlns:f="http://java.sun.com/jsf/core"
xmlns:w="http://www.apusic.com/jsf/widget" xmlns:layout="http://www.apusic.com/jsf/layout"
renderKitId="AJAX">
<w:page title="apply fee">
<w:form>
<layout:panelGrid columns="2">
<w:textField id="money"></w:textField>
<w:button id="apply" />
</layout:panelGrid>
</w:form>
</w:page>
</f:view>
后台的 ApplyBean 代码片断如下:
@ManagedBean(name="ApplyBean", scope=ManagedBeanScope.SESSION)
public class ApplyBean {
@Bind
private int money = 100;
@Action
public void apply(){
System.out.println("I'm a programmer, I apply " + money + " money");
}
}
基于 IoVC 原理,整个页面即可运行,效果如下:
现在,我们做一个新的托管Bean:ProjectManagerBean,代码如下:
@ManagedBean(name="ProjectManagerBean", scope=ManagedBeanScope.SESSION)
public class ProjectManagerBean {
private void applyFeeListener(int money) {
System.out.println("I'm your project manager, you apply " + money + " money.");
}
}
那么,我们如何通过事件机制,让这两个方法关联起来呢?
首先是发送事件,我们将代码修订如下:
@Action
@RaiseEvent("applyFee(this.money)")
public void apply(){
System.out.println("I'm a programmer, I apply " + money + " money");
}
请注意,我们增加了一个@RaiseEvent 的annotation,它的参数中:“applyFee”代表事件类型,“this.money”代表传递的参数。
ProjectManagerBean是怎样注册成为Listener的呢?
@EventListener("applyFee")
private void applyFeeListener(int money) {
System.out.println("I'm your project manager, you apply " + money + " money.");
}
上述代码含义就是:通过@EventListener这个annotation标明:applyFeeListener方法是个事件监听器方法,并且, 它所感兴趣的事件类型是:applyFee。
好了,我们来看一下程序运行的输出结果:
2008-03-01 14:57:30 信息 [con.out] I'm a programmer, I apply 100 money
2008-03-01 14:57:30 信息 [con.out] I'm your project manager, you apply 100 money.
两者之间已经通过事件关联起来了。
现在我们需要扩展一下:除了项目经理感兴趣,部门经理对此事也感兴趣,那么,我们根本无需更改任何代码,只是新增一个新的 DepartManagerBean:
@ManagedBean(name="DepartManagerBean", scope=ManagedBeanScope.SESSION)
public class DepartManagerBean {
@EventListener("applyFee")
private void applyFeeListener(int money) {
System.out.println("I'm your department manager, you apply " + money + " money.");
}
}
程序运行的输出结果如下:
2008-03-01 15:01:03 信息 [con.out] I'm a programmer, I apply 100 money
2008-03-01 15:01:03 信息 [con.out] I'm your department manager, you apply 100 money.
2008-03-01 15:01:03 信息 [con.out] I'm your project manager, you apply 100 money.
程序员申请费用,现在项目经理和部门经理都通过事件机制知道此事了。我们希望在同一个方法中,对事件进行分类: 如果费用小于 等于100,那么,就发送一个 applyLittleFee 的事件,如果费用大于100,则发送一个 applyMuchFee 的事件;项目经理关注 applyLittleFee 事件,而部门经理关注 applyMuchFee 的事件。ApplyBean修订如下:
public class ApplyBean {
@Bind
private int money = 100;
@Inject
private EventBroadcaster event;
@Action
public void apply(){
System.out.println("I'm a programmer, I apply " + money + " money");
if(money <=100) event.broadcast(this, "applyLittleFee", money);
else event.broadcast(this, "applyMuchFee", money);
}
}
此处的 EventBroadcaster (事件发送对象) 被注入,通过此对象,你对“发送什么事件”、“何时发送事件”都能够进行详细的控制。
ProjectManagerBean与DepartManagerBean修订如下:
@ManagedBean(name="ProjectManagerBean", scope=ManagedBeanScope.SESSION)
public class ProjectManagerBean {
@EventListener("applyLittleFee")
private void applyFeeListener(int money) {
System.out.println("I'm your project manager, you apply " + money + " money.");
}
}
@ManagedBean(name="DepartManagerBean", scope=ManagedBeanScope.SESSION)
public class DepartManagerBean {
@EventListener("applyMuchFee")
private void applyFeeListener(int money) {
System.out.println("I'm your department manager, you apply " + money + " money.");
}
}
请注意,ProjectManagerBean及DepartManagerBean所感兴趣的事件也做了相应的修订,程序运行输出如下:
2008-03-01 15:10:10 信息 [con.out] I'm a programmer, I apply 100 money
2008-03-01 15:10:11 信息 [con.out] I'm your project manager, you apply 100 money.
2008-03-01 15:10:14 信息 [con.out] I'm a programmer, I apply 1000 money
2008-03-01 15:10:14 信息 [con.out] I'm your department manager, you apply 1000 money.
有时候会有这样一种需求:程序员申请费用,根据费用的大小,要自动导航到不同的页面。但有时候, 要重定向到哪些页面我们事先并不清楚。以上述场景为例:当项目经理审批时,希望导航到 projectManager.xhtml页面, 当部门经理审批时,希望导航到 departManager.xhtml页面。在 AOM 2.0中,如何解决此需求?
我们首先完成这两个页面:
projectManager.xhtml:
<f:view xmlns="http://www.w3.org/1999/xhtml" xmlns:f="http://java.sun.com/jsf/core"
xmlns:w="http://www.apusic.com/jsf/widget" xmlns:layout="http://www.apusic.com/jsf/layout"
renderKitId="AJAX" xmlns:h="http://java.sun.com/jsf/html">
<w:page title="project manager">
I'm your project manager, you apply <h:outputText id="money"/> money.
</w:page>
</f:view>
departManager.xhtml
<f:view xmlns="http://www.w3.org/1999/xhtml" xmlns:f="http://java.sun.com/jsf/core"
xmlns:w="http://www.apusic.com/jsf/widget" xmlns:layout="http://www.apusic.com/jsf/layout"
renderKitId="AJAX" xmlns:h="http://java.sun.com/jsf/html">
<w:page title="project manager">
I'm your depart manager, you apply <h:outputText id="money"/> money.
</w:page>
</f:view>
再对 ProjectManagerBean及DepartManagerBean修订如下:
@ManagedBean(name="ProjectManagerBean", scope=ManagedBeanScope.SESSION)
public class ProjectManagerBean {
@Bind
private int money;
@EventListener("applyLittleFee")
private String applyFeeListener(int money) {
this.money = money;
return "view:projectManager";
}
}
@ManagedBean(name="DepartManagerBean", scope=ManagedBeanScope.SESSION)
public class DepartManagerBean {
@Bind
private int money;
@EventListener("applyMuchFee")
private String applyFeeListener(int money) {
this.money = money;
return "view:departManager";
}
}
请注意观察:return "view:projectManager"语句中,前面的 view 意即告诉AOM,这是要导航,后面的projectManager代表 view 的ID。
程序运行效果如下:
我们只是通过上述例子了解了AOM 2.0中模型事件的一些小特性,事实上,AOM 2.0中模型事件是比较完备的,还有许多其它特性我们并没有介绍,譬如,我们可以在 Action 中直接通过return 语句触发事件:
return "#applyFee(this.money)";
在 @RaiseEvent中,我们还可以引用传递给方法的参数:
@RaiseEvent("showResponse($1.name)")
public void loginSuccess(User user) {
...
}
这些特性,还请各位读者参考官方文档。
本章节,我们简单体验了 AOM 2.0 中“模型事件”的概念。笔者以为:事件是一把双刃剑,运用得好,那么,能够大大增强程序的灵活性及可扩展性; 但如果滥用事件,则只会让你的程序难以跟踪,难以理解。
相关推荐
《深入理解OperaMasks UI 2.0:前端框架与应用实践》 OperaMasks UI 2.0是一款由金蝶公司推出的高效、易用的前端界面库,它旨在为开发者提供一套完整的用户界面解决方案,以提升Web应用程序的用户体验和开发效率。...
operamasks的作用手册
"Operamasks UI 2.0 Doc"是一个针对 Operamasks 用户界面的开发文档,它提供了详尽的指导和信息,帮助开发者理解和构建基于Operamasks的Web应用程序。这个离线版文档对于开发者来说尤其珍贵,因为在线寻找这类资源...
operamasks-ui-2.0 这个帮助文档很难才找到的,感谢CSDN,其中的说明真的是很详细了,维护旧代码用到的这个框架,相比easyui和bootstrap这个框架简单一些,不过用起来还是不错的,除了文档太少,不过有这个就基本...
"Operamasks UI 2.0 Demo"是一个与JavaScript相关的压缩包,包含了对Opera Masks用户界面新版本的演示。这个项目可能是一个Web应用程序框架或库,专为开发人员设计,以便在Opera浏览器或其他支持JavaScript的环境中...
"Operamasks UI 2.0 Demo" 是一个专门针对Opera浏览器的扩展或应用界面设计的开发套件,主要用于创建和定制用户界面。这个压缩包文件 "operamasks-ui-2.0-demo--.zip" 包含了用于演示和实践如何使用Opera Masks UI ...
IoVC是OperaMasks2.0引入的概念,旨在解决传统MVC模式中控制逻辑与展现逻辑难以完全解耦的问题。在IoVC模型中,控制逻辑不再由展现层触发,而是由后台业务逻辑控制,这样开发者可以通过绑定ID来实现展现层与业务逻辑...
只是我在网上找的 operamasks-ui api 文档 , 希望对你们有帮助
【标题】"OperaMasks查询、模糊查询、源码"涉及的是一个基于OperaMasks前端框架和后端servlet+bean技术实现的查询系统。在这个Demo中,开发者展示了如何运用这些技术来创建一个具备模糊查询功能的应用。让我们深入...
**Operamasks SDK 3.2:金蝶中间件的创新解决方案** Operamasks SDK 3.2 是金蝶中间件公司推出的一款重要的软件开发工具包,专为开发者设计,旨在简化与金蝶产品集成的过程,提高开发效率,并增强应用程序的功能。...
它简化了开发过程,提供了一种组件模型,允许开发者通过拖放组件来创建用户界面。**OperaMasks** 是一个专为Opera浏览器设计的JSF组件库,它扩展了JSF的功能,使得在Opera中使用JSF应用更加顺畅。 **教程概述:** ...
#### 四、使用operaMasks_studio开发JSF项目 1. **创建新项目**:打开IDE后,选择“新建”->“JSF项目”,根据提示填写项目名称、存储位置等基本信息,并选择合适的JSF版本。 2. **设计用户界面**:利用...
【标题】"operamasks-ui-master.zip" 是一个包含了 OperaMasks UI 2.0 框架的源代码压缩包。这个框架是基于前端开发的,尤其适用于构建企业级的 Web 应用程序,其设计目标是提高开发效率,提供良好的用户体验。...
**OperaMasks安装包详解** OperaMasks 是一个专为Opera浏览器设计的扩展程序,它提供了丰富的功能,旨在提升用户的浏览体验。这个安装包包含了多个核心组件,让我们逐一解析: 1. **operamasks-comp.jar**:这个...
在探索这个Demo时,开发者可以学习到如何利用OperaMasks API进行扩展开发,包括如何添加和管理扩展的菜单项、工具栏按钮,以及如何处理用户事件。此外,UI设计者可以从示例中获取灵感,了解如何创建美观且用户友好的...
这可能涉及到监听用户交互事件,更新数据模型,以及调整DOM元素的显示状态。 4. 对于detail属性,可能需要创建一个新的模板或组件来展示详细信息,并在grid单元格上添加交互元素(如图标或按钮)来触发细节显示。 5....