什么是Drools
(译者增加:什么是Drools, 摘自drools.org)
Drools 是一个基于Charles Forgy's的Rete算法的,专为Java语言所设计的规则引擎。Rete算法应用于面向对象的接口将使基于商业对象的商业规则的表达更为自然。Drools是用Java写的,但能同时运行在Java和.Net上。
Drools
Drools 被设计为可插入式的语言实现。目前规则能用Java, Python和Groovy实现。更为重要的是,Drools提供了声明式程序设计(Declarative Programming),并且使用域描述语言(Domain Specific Languages (DSL))-专为你的问题域定义了某种模式的Xml, 它已经足够灵活到可以用来描述你的问题域。DSLs包含的XML元素(Element)和属性(Attribute)代表了问题域中各种要素。
(原文开始)
这 段时间企业级Java简直能让你睡着。有多少J2EE-EJB应用程序只是从网页提取数据并把它们存入到数据库中?但开发者真正应该开发和维护的却是他们 应用程序中复杂的商业逻辑。这个问题不仅仅适用于将要新应用,而且渐渐地,也适用于那些长期运行的商业核心应用,它们的内部逻辑也需要经常改变,而且往往 要求在一个非常短的时间内。
在以前的文章中,“用Drools让你的商业逻辑使用框架”,我介绍了Drools框架,展示了它如何用来组 织复杂的商业逻辑。Drool用一组简单的,众所周知的事物替换了许多缠杂的if…then表达式。如果你经历过和商业客户的会议,并为他们提出的想要实 现的东西的复杂程度搞得头痛,或许你应该考虑一下像Drools这样的规则引擎了。这篇文章将向你展示如何在企业级Java应用中使用Drools.
一路到底的框架
大 多数开发者都有自己喜爱的框架。无特定顺序,它们包括表现层框架(Struts, JSF, Cocoon和Spring),持久化框架(JDO, Hibernate, Cayenne and Entity Beans)以及结构框架(EJB, 又是Spring, Pico和Excalibur), 还有其它很多。每种框架都各有所长,给开发者提供子许多“即开即用”的功能。使用框架来部署应用意味着你避免了许多让人厌烦的细节,让你集中注意力到关键 之处。
到目前为直,在框架所能做的事中仍然有一个缺口,那就是商业逻辑没有框架。像EJB和Spring这样的工具虽好,但它们却几乎没有提及怎 么组织你的那些if …then语句。把Drools加到你的开发工具箱中意味着现在你可以“一路到底”的使用框架来构建你的应用程序。图1显示了这样的一个应用
图1. 用于Java应用的框架
这篇文章将基于我们已经了解的Drools框架的功能,这些功能可以让我们构建这样的一个应用。]
我什么时候应该使用规则引擎?
“如果你有一把锤子,那所有的东西都看起来都像钉子”,这句话在软件工程领域几乎成了陈词滥调了。虽然规则引擎能解决我们的许多问题,但确实值得认真考虑一下规则引擎对我们的企业级Java应用是否合适。需要问的问题有:
● 我的应用程序有多复杂?对于那些只是把数据从数据库中传入传出,并不做更多事情的应用程序,最好不要使用规则引擎。但是,当在Java中有一定量的商业逻辑处理的话,可以考虑Drools的使用。这是因为很多应用随着时间的推移越来越复杂,而Drools可以让你轻松应对这一切。
● 我的应用的生命周期有多久?这个问题的正确答案往往是“令人惊讶的长”――还记得那些认为他们的程序不会苟活到2000年的大型机的程序员吗?使用规则引擎将会在中长期得到好处。像这篇文章所展示的那样,甚至原型都能从Drools与灵活方法的组合中获益,让“原型系统”转化成生产系统。
● 我的应用需要改变吗?唯一能确定的是你的需求将会改变,无论是在开发过程中或是在开发完成以后。Drools使用一个或多个简单易配的XML文件帮你来应对这一切。
那么性能呢?
如 果你正在写一个企业级应用,很有可能它会扩展到成百(如果不是成千)的用户。你已经知道现有的Java和J2EE应用能做到这一点,但一个使用了 Drools的应用对这一压力的表现如何?答案是:“令人吃惊的好”。大多数开发者只是因为不愿“失控”而依赖于他人的代码(比如:某种框架),想想这 个:Drools不仅可以让你的应用和“传统”的编程方法一样快,甚至可以更快,看下面:
● 避免糟糕的代码:Drools引导开发者去做“正确的事”。你可以确定你正在写的代码是好的,但你的开发伙伴呢?你可以同样这样说吗?使用框架可以让你更轻松地写出更快,更好的代码。
● 优化过的框架:你有多少次看见商业逻辑重复地从数据库中提取相同的信息,从而降低了整个应用的速度?如果正确使用的话,Drools不仅仅能够记住信息,而且还能记住以往使用该信息进行测试的结果,从而大幅提升应用的速度。
● Rete算法: 很多次我们并不是真正需要使用“if”条件。被Drools实现的Rete算法,可以用一个优化的方法替换掉所有的“if…then”表达式。需要重点提 及的是:Rete算法在使用更多的内存来降低运行时延迟方面作了折衷。当然这在现代的应用服务器中并不是一个问题,我们也并不推荐你在移动手机上使用 Drools!
我们到哪里了?
在 我们上一篇文章中,我们写了一个基于Drools引擎的简单的股票交易程序。我们实现了不同的商业规则,展示了我们可以如何迅速地改变规则去适应商业需 求,并且JUnit测试给了我们高度自信可以确认系统确实是像我们设想的那样运作的。但是这个应用几乎没有用户介面,而且用硬编码代替了数据库。为了把我 们的程序提升到企业级的水平,我们需要增加两个主要的东西。
● 某种用户介面,最理想的是基于标准的Web表现层的框架。
● 一个数据存取对象(DAO)让Drools与数据库(或其它后端)交互。
从现有表现框架中实现规则引擎
大 多数企业级Java应用是通过Web介面进行交互的,其中最被广泛使用的Web表现层框架是Apache的Struts。理想的结果是:我们写的应用可以 从表现层知道它下面的应用层,而不是通过相反的方向。它的好处在于不仅仅可以使我们将来变换其它的表现层(比如Ajax或web service界面),而且意味着示例代码可以非常容易地应用于其它像Spring的框架。
下面的代码片断演示了始何从Web表现层调用 商业逻辑(通过规则引擎),并根据返回结果显示不同的页面。这一例子中,我们使用了一个Struts行为,但其代码是和使用其它表现层框架甚至一个 Servlet或一个Jsp页面是很类似的。这个片断使用了struts-config.xml配置文件,JSP页面来上传/显示数据,并且生成WAR文 件来进行布署。片断展示了怎样把规则引擎和web框架集成使用。
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.struts.action.Action;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
import BusinessLayer;
/**
* Sample Struts action with Pseudocode
* 使用伪代码的Struts行为示例
*/
public class SampleStrutsAction extends Action{
/**
* Standard Struts doPerfom method
* 标准的Struts doPerform方法
*/
public ActionForward doPerform(
ActionMapping mapping,
ActionForm form,
HttpServletRequest request,
HttpServletResponse response)
throws InvalidEntryPointException {
//Local Variables
//本地变量
StockOffer userOffer =null;
//Get any previous values from the session
//从session取得以前的数据
userOffer=(StockOffer)request.getSession()
.getAttribute("PREVIOUS_STOCK_OFFER");
//create this object if it is null
//如为null则创建新对象
if (null==userOffer){
userOffer = new StockOffer();
}
//Update with the incoming values
//用上送的数据更新
//These values match those on the form
//这些数据是与form中的数据相对应的
userOffer.setStockName(request.
getParameterValue("STOCK_NAME"));
userOffer.setStockPrice(request
.getParameterValue("STOCK_PRICE"));
userOffer.setStockQuantity(request
.getParameterValue("STOCK_QTY"));
//Reset the output value
//重置输出数据
userOffer.setRecommendPurchase(null);
//Call the Business Layer
//调用商业层
BusinessLayer
.evaluateStockPurchase(userOffer);
//Forward to the appropriate page
//转向合适的页面
if ("YES".equals(
testOffer.getRecommendPurchase()){
return mapping.findForward("YES_WEB_PAGE");
}
//otherwise default to the no page
//否则指向无此页面
return mapping.findForward("NO_WEB_PAGE");
}
}
/**
* Defines a Data Access Object - a non data
* source specific way of obtaining data.
* 定义一个数据存取对象-一种非数据源获取数据的方法
*/
public interface StockNameDao {
/**
* Get a list of stock names for the application
* @return String[] array of stock names
* 得到一个股票名字的列表
* 返回股票名称的String[]数组
*/
public String [] getStockNames();
/**
* Check if our stock is on the list
* 检查股票是否在列表中
* @param stockName
* @return
*/
public boolean isOnStockList(String stockName);
}
And here's the DaoImplementation:
这是DaoImplementation:
/**
* Concrete Definition of a Data Access Object
* 数据存取对象的具体定义
*/
public class DaoImplementation
implements StockNameDao {
/**
* Constructor with package level access only
* to encourage use of factory method
* 这里的构造器只是让你使用工厂(factory)方法
*/
DaoImplementation(){}
/**
* Get a list of stock names for the app.
* This is a hard coded sample
* normally we would get this from
* a database or other datasource.
* 得到一个股票名字的列表,这只是一个硬编码的例子,一般来
* 说我们应该从数据库或其它数据源取得数据
* @return String[] array of stock names
*/
public String[] getStockNames() {
String[] stockNames=
{"XYZ","ABC","MEGACORP","SOMEOTHERCOMPANY"};
return stockNames;
}
/**
* Check if our stock is on the list
* 检查我们的股票是否在列表中
* @param stockName
* @return true / false as appropriate
*/
public boolean isOnStockList(String stockName){
//Get our list of stocks
//获取股票列表
String stockList[] = getStockNames();
//Loop and see if our stock is on it
// done this way for clarity . not speed!
//循环看股票是否存在,这样做是为了清晰不是速度!
for (int a=0; a<stockList.length;a++){
if(stockList[a].equals(stockName)){
return true;
}
}
//Default return value
return false;
}
}
package net.firstpartners.rp;
/**
* Factory Method to get the Data Access Object.
* Normally we could replace this with a
* framework like Spring or Hibernate
* 得到数据存取对象的工厂方法,通常我们可以将它替换为像Spring或
* Hibernatte这样的框架
*/
public class DaoFactory {
/**
* Get the stock name Dao
* This sample is hardcoded - in reality
* we would make this configurable / cache
* instances of the Dao as appropriate
* 得到股票名字的Dao,这个例子是硬编码的-实际上我们可以让它成为
* 可配的,缓存的合适的Dao对象。
* @return an instance of StockNameDao
*/
public static StockNameDao getStockDao(){
return new DaoImplementation();
}
}
<?xml version="1.0"?>
<rule-set name="BusinessRulesSample"
xmlns="http://drools.org/rules"
xmlns:java="http://drools.org/semantics/java"
xmlns:xs="
http://www.w3.org/2001/XMLSchema-instance"
xs:schemaLocation="
http://drools.org/rules rules.xsd
http://drools.org/semantics/java java.xsd">
<!-- Import the Java Objects that
we refer to in our rules -->
<!-- 导入规则中使用的对象 -->
<java:import>
java.lang.Object
</java:import>
<java:import>
java.lang.String
</java:import>
<java:import>
net.firstpartners.rp.StockOffer
</java:import>
<java:import>
net.firstpartners.rp.DaoFactory
</java:import>
<java:import>
net.firstpartners.rp.StockNameDao
</java:import>
<!-- Application Data not associated -->
<!-- with any particular rule -->
<!-- In this case it's our factory -->
<!-- object which gives us back -->
<!-- a handle to whatever Dao (Data -->
<!-- access object) that we need -->
<!-- 没有和任何规则联系的应用数据,这里是我们的工厂对象,-->
<!—它向我们提供向后的操作,告诉我们什么Dao 是我们需要的。-->
<application-data
identifier="daoFactory">DaoFactory
</application-data>
<!-- A Java (Utility) function -->
<!-- 一个Java方法 -->
<!-- we reference in our rules -->
<!-- 在我们的规则中打印跟踪信息 -->
<java:functions>
public void printStock(
net.firstpartners.rp.StockOffer stock)
{
System.out.println(
"Name:"+stock.getStockName()
+" Price: "+stock.getStockPrice()
+" BUY:"+stock.getRecommendPurchase());
}
</java:functions>
<!-- Check for XYZ Corp-->
<!-- 检查XYZ公司 -->
<rule name="XYZCorp" salience="-1">
<!-- Parameters we can pass into-->
<!-- the business rule -->
<!-- 可以传入规则中的参数 -->
<parameter identifier="stockOffer">
<class>StockOffer</class>
</parameter">
<!-- Conditions that must be met for -->
<!-- business rule to fire -->
<!-- 激活规则必须满足的条件 -->
<java:condition>
stockOffer.getStockName().equals("XYZ")
</java:condition>
<java:condition>
stockOffer.getRecommendPurchase() == null
</java:condition>
<java:condition>
stockOffer.getStockPrice() > 10
</java:condition>
<!-- What happens when the business -->
<!-- rule is activated -->
<!-- 规则激活后执行的步骤 -->
<java:consequence>
stockOffer.setRecommendPurchase(
StockOffer.NO);
printStock(stockOffer);
</java:consequence>
</rule>
<!-- Ensure that negative prices -->
<!-- are not accepted -->
<!-- 确定负数不被接受 -->
<rule name="Stock Price Not Negative">
<!-- Parameters we can pass into the -->
<!-- business rule -->
<!-- 可以传入规则中的参数 -->
<parameter identifier="stockOffer">
<class>StockOffer</class>
</parameter>
<!-- Conditions for rule to fire -->
<!-- 激活规则必须满足的条件 -->
<java:condition>
stockOffer.getStockPrice() < 0
</java:condition>
<!--When rule is activated then ... -->
<!-- 规则激活后执行的步骤 -->
<java:consequence>
stockOffer.setRecommendPurchase
(StockOffer.NO);
printStock(stockOffer);
</java:consequence>
</rule>
<!-- Check for Negative Prices-->
<!-- 寻找低价 -->
<rule name="Stock Price Low Enough">
<!-- Parameters for the rule -->
<!-- 可以传入规则中的参数 -->
<parameter identifier="stockOffer">
<class>StockOffer</class>
</parameter>
<!-- Now uses Dao to get stock list -->
<!-- 现在使用Dao获取股票列表 -->
<java:condition>
daoFactory.getStockDao().isOnStockList(
stockOffer.getStockName())
</java:condition>
<java:condition>
stockOffer.getRecommendPurchase() == null
</java:condition>
<java:condition>
stockOffer.getStockPrice() < 100
</java:condition>
<!-- When rule is activated do this -->
<!-- 规则激活后执行的步骤 -->
<java:consequence>
stockOffer.setRecommendPurchase(
StockOffer.YES);
printStock(stockOffer);
</java:consequence>
</rule>
</rule-set>
Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=1358774