摘要
这篇文章解释了在JEE应用中规则引擎及声明性业务逻辑的优点并且描述如何为流行的Spring框架开发简单的规则引擎此文需要读者对Spring有基本的了解
任何大一点的软件项目都包含了许多叫做业务逻辑的东西业务逻辑的准确描述还是有争议的在为典型应用软件的生成的大量代码中到处都是为如订单处理武器控制系统图形绘制等功能工作的零碎代码这些代码与其他如处理持久化日志事务语言偏好框架特性及其他现代企业级应用有明显不同
业务逻辑通常与其他代码块紧密的混和在一起当重量级的侵入式框架(如EJB)被使用时区别业务逻辑与框架生成的代码就变得非常困难
有一个软件需求在需求定义文档很难准确描述却拥有使软件项目成功或失败的能力适应性这是用来衡量软件响应业务变更容易程度的标准
现代企业要求响应快速及灵活他们对企业软件也有同样的要求可能你今天辛苦实现的业务规则在明天就被废弃了而且要求你根据变更快速而准确的改变当你的包含业务逻辑的代码隐藏在大量其他代码中时修改就变得缓慢痛若且易出错了
在今天的企业级软件中没有奇迹比较流行的是规则引擎和各种业务过程管理(BPM)系统如果你看一下市场上的宣传这类工具都承诺一件事保存在仓库中的捕获业务逻辑的圣杯能够清晰的分离且由自己维护并随时准备让你现有的应用来调用
虽然商业的规则引擎和BPM系统有许多优点但也有不少缺点最大的缺点就是价格通常很容易就达到位数另一个就是除了主要的行业规范和众多记在纸上的标准外缺乏事实上的标准而且随着越来越多的软件项目采用敏捷轻量级的快速开发方法这些重量级的工具变得不符合潮流了
在这篇文章中我们建立了一个简单的规则引擎一方面平衡系统与业务逻辑的分离另一方面由于他基于目前流行的强大的JEE框架因而不需要承受商业软件的复杂性与不协调性
JEE世界中的Spring时代
在企业级软件的复杂性变得不能忍受及业务逻辑问题越来越重要时Spring及类似的框架产生了可以断定Spring在以后很长一段时间内是企业级Java中的佼佼者Spring提供了很多工具及少量代码约定使JEE的开发更面向对象更容易也更有趣
Spring的核心是IoC原则这是一个奇特而超负荷的名字但包含下面的简单想法
●功能代码需要分开到更小的可管理片断
●这些片断是简单的标准的JavaBean(简单的Java类拥有但不包含全部的JavaBean规范)
●你不需要参与管理这些Bean(如创建销毁设置依赖)
●相反Spring容器通过上下文定义来为你做这些(通常为XML文件格式)
Spring也提供了很多其他特性如完整而强大的MVC框架简便的JDBC开发包装及其他框架但那些主题已经超出这篇幅文章的讨论范围
在我描述需要什么来创建基于SPRING应用的简单规则引擎之前让我们想一下为什么这是一种好的想法
规则引擎设计有两点有趣的特性使其更有价值
●首先从应用领域分离了业务逻辑代码
●其次可配置性意味着业务规则的定义及其使用的顺序被存储在应用的外部这样就可以由规则创建人员来控制而不是应用的使用者或者开发人员了
Spring为规则引擎提供了一个好的方法一个良好编码的Spring应用的强组件化的设计会使你的代码变成更小的可管理的分散片断这样就更易在Spring的上下文定义中配置
继续了解在规则引擎设计的需求与Spring设计提供的功能之间的结合点
基于Spring的规则引擎的设计
我们在Spring控制的JavaBean基础上开始设计这里我们叫做规则引擎组件我们来定义下面两种我们可能需要的组件类型
●操作—在应用逻辑中确定用来做什么的组件
●规则—在一系列行为的逻辑流中做出决定的组件
我们都是面向对象设计的追随者下面的基类建立了所有我们的组件需要通过参数被其他组件调用的基本功能
public abstract class AbstractComponent { public abstract void execute(Object arg) throws Exception; }
当然基类是抽象的因为我们根本不需要这样的实例
AbstractAction的代码扩展了基类来实现其他具体的操作
public abstract class AbstractAction extends AbstractComponent { private AbstractComponent nextStep;public void execute(Object arg) throws Exception {thisdoExecute(arg);if(nextStep != null) nextStepexecute(arg); }protected abstract void doExecute(Object arg) throws Exception; public void setNextStep(AbstractComponent nextStep) {thisnextStep = nextStep; } public AbstractComponent getNextStep() {return nextStep; }}
你可以看到AbstractAction做两件事首先他保存在规则引擎中被激活的下一个组件的定义其次在他的execute()方法中调用被具体类实现的doExecute()方法在doExecute()返回后如果存在下一个组件则调用他
我们的AbstractRule也相当简单
public abstract class AbstractRule extends AbstractComponent { private AbstractComponent positiveOutcomeStep; private AbstractComponent negativeOutcomeStep;public void execute(Object arg) throws Exception {boolean outcome = makeDecision(arg);if(outcome) positiveOutcomeStepexecute(arg);else negativeOutcomeStepexecute(arg); } protected abstract boolean makeDecision(Object arg) throws Exception;
// 为简单起见positiveOutcomeStep和negativeOutcomeStep 的Getters 和 setters均已省略
在其execute()方法中AbstractAction调用由子类实现的makeDecision()方法然后根据方法的返回值调用组件定义的肯定或否定结果的方法
在我们介绍了SpringRuleEngine类后我们的设计就基本完成了
public class SpringRuleEngine {private AbstractComponent firstStep;public void setFirstStep(AbstractComponent firstStep) {thisfirstStep = firstStep; }public void processRequest(Object arg) throws Exception {firstStepexecute(arg); } }
这就是我们规则引擎主类的全部定义第一个业务逻辑中的组件及开始执行的方法
但是请稍等在哪里绑定我们的类使之可以工作呢?下面你就可以看到如何利用Spring来帮助我们完成工作的方法了
在操作中的基于Spring的规则引擎
让我们看一下这个框架如何工作的具体实例吧想象下面的用例我们需要开发负责贷款申请的应用程序我们需要满足下面的条件
●检查应用的完整性否则驳回
●检查应用是否来自我们授权处理业务的应用
●检查申请者的月收支比是否满足我们的要求
●输入的申请通过我们不知道实现细节的持久服务被存储在数据库中我们只知道他的接口(可能这个开发被外包到印度了)
●业务规则是可以改变的这也是为什么需要规则引擎的设计了
首先设计一个表示贷款申请的类
public class LoanApplication {public static final String INVALID_STATE = Sorry we are not doing business in your state; public static final String INVALID_INCOME_EXPENSE_RATIO = Sorry we cannot provide the loan given this expense/income ratio; public static final String APPROVED = Your application has been approved; public static final String INSUFFICIENT_DATA = You did not provide enough information on your application; public static final String INPROGRESS = in progress;public static final String[] STATUSES = new String[] { INSUFFICIENT_DATA INVALID_INCOME_EXPENSE_RATIO INVALID_STATE APPROVED INPROGRESS}; private String firstName; private String lastName; private double income; private double expences; private String stateCode; private String status;public void setStatus(String status) {if(!ArraysasList(ntains(status)) throw new IllegalArgumentException(invalid status: + status);thisstatus = status; }// 其他getters and setters已被省略}
我们使用的持久服务拥有如下接口
public interface LoanApplicationPersistenceInterface { public void recordApproval(LoanApplication application) throws Exception; public void recordRejection(LoanApplication application) throws Exception; public void recordIncomplete(LoanApplication application) throws Exception;}
我们迅速开发一个什么也不做只是用来满足接口约定的MockLoanApplicationPersistence类来欺骗接口
我们使用下面的SpringRuleEngine类的子类来加载Spring上下文并开始处理
public class LoanProcessRuleEngine extends SpringRuleEngine { public