Drools rule engine example

Irregularities do not form a circle, and the same is true in the software field. For example, the most common "membership points rule". Member registration has an original point. This is easy to handle. Initialize an original value when creating a new object. Then how many original values ​​are there? Such as normal registration, holiday registration, etc., the original value will be different. This is a rule; there is also the effective date of registration, such as effective immediately, effective within three days, etc. This is also a rule; member consumption There will also be multiple rules, such as ordinary consumption, holiday consumption, and birthday consumption; membership consumption times and discounts will also produce multiple rules, such as holiday consumption and ordinary consumption discounts, and daily consumption discounts are different..... ..

In this way, the rules can be described as diverse and dizzy, and beginners may judge infinitely with a few if/else, then this may be done, let alone the difficulty of future maintenance (even sometimes you don’t To the source file), your executive body alone is enough to be swollen; people with a little experience may choose some design patterns, such as factory pattern, adapter pattern, etc., and then there is a problem, just a rule You produce countless implementation objects. If there are more such rules, then your system is huge enough to suffocate people.

So we will wonder if there is a way for us to modify a few configuration files to solve these problems, without affecting the structure of the entire system, that is, to achieve the so-called loose coupling? So Drools came into being!

Drools is an open source business under JBOOS. Open source things are exciting, because it is not only free but also allows us programmers to give full play to what we have seen.

No nonsense, let us implement a small example of membership points, the mystery will naturally appear:

To build a project under eclipse, because I just sorted out the implementation steps for everyone, so I only built a simple java project:

Import the j related ar package required by drools, see the screenshot:

 

Then we add a java class to declare the parameters of the integration first:

package com.drools.demo.point;


/**
 * 积分计算对象
 * @author quzishen
 */
public class PointDomain {
	// 用户名
	private String userName;
	// 是否当日生日 
	private boolean birthDay;
	// 增加积分数目
	private long point;
	// 当月购物次数
	private int buyNums;
	// 当月退货次数
	private int backNums;
	// 当月购物总金额
	private double buyMoney;
	// 当月退货总金额
	private double backMondy;
	// 当月信用卡还款次数
	private int billThisMonth;
	public boolean isBirthDay() {
		return birthDay;
	}

	public void setBirthDay(boolean birthDay) {
		this.birthDay = birthDay;
	}

	public long getPoint() {
		return point;
	}

	public void setPoint(long point) {
		this.point = point;
	}

	public int getBuyNums() {
		return buyNums;
	}

	public void setBuyNums(int buyNums) {
		this.buyNums = buyNums;
	}

	public int getBackNums() {
		return backNums;
	}

	public void setBackNums(int backNums) {
		this.backNums = backNums;
	}

	public double getBuyMoney() {
		return buyMoney;
	}

	public void setBuyMoney(double buyMoney) {
		this.buyMoney = buyMoney;
	}

	public double getBackMondy() {
		return backMondy;
	}

	public void setBackMondy(double backMondy) {
		this.backMondy = backMondy;
	}

	public int getBillThisMonth() {
		return billThisMonth;
	}

	public void setBillThisMonth(int billThisMonth) {
		this.billThisMonth = billThisMonth;
	}

	public void setUserName(String userName) {
		this.userName = userName;
	}
	public String getUserName() {
		return userName;
	}
	/**
	 * 记录积分发送流水,防止重复发放
	 * @param userName 用户名
	 * @param type 积分发放类型
	 */
	public void recordPointLog(String userName, String type){
		System.out.println("增加对"+userName+"的类型为"+type+"的积分操作记录.");
	}
}

After defining the rule object, then we have to define its rules, create a new rule file in the src directory, addpoint.drl

edit:

package com.drools.demo.point

import com.drools.demo.point.PointDomain;
 
rule birthdayPoint
	// 过生日,则加10分,并且将当月交易比数翻倍后再计算积分
	salience 100
	lock-on-active true
	when
		$pointDomain : PointDomain(birthDay == true)
	then
		$pointDomain.setPoint($pointDomain.getPoint()+10);
		$pointDomain.setBuyNums($pointDomain.getBuyNums()*2);
		$pointDomain.setBuyMoney($pointDomain.getBuyMoney()*2);
		$pointDomain.setBillThisMonth($pointDomain.getBillThisMonth()*2);
		
		$pointDomain.recordPointLog($pointDomain.getUserName(),"birthdayPoint");
end

rule billThisMonthPoint
	// 2011-01-08 - 2011-08-08每月信用卡还款3次以上,每满3笔赠送30分
	salience 99
	lock-on-active true
	date-effective "2011-01-08 23:59:59"
	date-expires "2011-08-08 23:59:59"
	when
		$pointDomain : PointDomain(billThisMonth >= 3)
	then
		$pointDomain.setPoint($pointDomain.getPoint()+$pointDomain.getBillThisMonth()/3*30);
		$pointDomain.recordPointLog($pointDomain.getUserName(),"billThisMonthPoint");
end

rule buyMoneyPoint
	// 当月购物总金额100以上,每100元赠送10分
	salience 98
	lock-on-active true
	when
		$pointDomain : PointDomain(buyMoney >= 100)
	then
		$pointDomain.setPoint($pointDomain.getPoint()+ (int)$pointDomain.getBuyMoney()/100 * 10);
		$pointDomain.recordPointLog($pointDomain.getUserName(),"buyMoneyPoint");
end

rule buyNumsPoint
	// 当月购物次数5次以上,每五次赠送50分
	salience 97
	lock-on-active true
	when
		$pointDomain : PointDomain(buyNums >= 5)
	then
		$pointDomain.setPoint($pointDomain.getPoint()+$pointDomain.getBuyNums()/5 * 50);
		$pointDomain.recordPointLog($pointDomain.getUserName(),"buyNumsPoint");
end

rule allFitPoint
	// 特别的,如果全部满足了要求,则额外奖励100分
	salience 96
	lock-on-active true
	when
		$pointDomain:PointDomain(buyNums >= 5 && billThisMonth >= 3 && buyMoney >= 100)
	then
		$pointDomain.setPoint($pointDomain.getPoint()+ 100);
		$pointDomain.recordPointLog($pointDomain.getUserName(),"allFitPoint");
end

 

We need to define an engine factory class to generate the engine tools we need

import org.drools.RuleBase;
import org.drools.RuleBaseFactory;

/**
 * RuleBaseFacatory 单实例RuleBase生成工具
 * @author quzishen
 */
public class RuleBaseFacatory {
	private static RuleBase ruleBase;
	
	public static RuleBase getRuleBase(){
		return null != ruleBase ? ruleBase : RuleBaseFactory.newRuleBase();
	}
}


 

Next, we define an interface for the integration rules and complete three things: initialize the integration rules, execute the rules, and refresh the rules.

import com.drools.demo.point.PointDomain;

/**
 * 规则接口
 * @author quzishen
 */
public interface PointRuleEngine {
	
	/**
	 * 初始化规则引擎
	 */
	public void initEngine();
	
	/**
	 * 刷新规则引擎中的规则
	 */
	public void refreshEnginRule();
	
	/**
	 * 执行规则引擎
	 * @param pointDomain 积分Fact
	 */
	public void executeRuleEngine(final PointDomain pointDomain);
}


Implementation class:

import java.io.File;

/**
 * 规则接口实现类
 * @author quzishen
 */
public class PointRuleEngineImpl implements PointRuleEngine {
	private RuleBase ruleBase;

	/* (non-Javadoc)
	 * @see com.drools.demo.point.PointRuleEngine#initEngine()
	 */
	public void initEngine() {
		// 设置时间格式
		System.setProperty("drools.dateformat", "yyyy-MM-dd HH:mm:ss");
		ruleBase = RuleBaseFacatory.getRuleBase();
		try {
			PackageBuilder backageBuilder = getPackageBuilderFromDrlFile();
			ruleBase.addPackage(backageBuilder.getPackage());
		} catch (DroolsParserException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	/* (non-Javadoc)
	 * @see com.drools.demo.point.PointRuleEngine#refreshEnginRule()
	 */
	public void refreshEnginRule() {
		ruleBase = RuleBaseFacatory.getRuleBase();
		org.drools.rule.Package[] packages = ruleBase.getPackages();
		for(org.drools.rule.Package pg : packages) {
			ruleBase.removePackage(pg.getName());
		}
		
		initEngine();
	}

	/* (non-Javadoc)
	 * @see com.drools.demo.point.PointRuleEngine#executeRuleEngine(com.drools.demo.point.PointDomain)
	 */
	public void executeRuleEngine(final PointDomain pointDomain) {
		if(null == ruleBase.getPackages() || 0 == ruleBase.getPackages().length) {
			return;
		}
		
		StatefulSession statefulSession = ruleBase.newStatefulSession();
		statefulSession.insert(pointDomain);
		
		// fire
		statefulSession.fireAllRules(new org.drools.spi.AgendaFilter() {
			public boolean accept(Activation activation) {
				return !activation.getRule().getName().contains("_test");
			}
		});
		
		statefulSession.dispose();
	}

	/**
	 * 从Drl规则文件中读取规则
	 * @return
	 * @throws Exception
	 */
	private PackageBuilder getPackageBuilderFromDrlFile() throws Exception {
		// 获取测试脚本文件
		List<String> drlFilePath = getTestDrlFile();
		// 装载测试脚本文件
		List<Reader> readers = readRuleFromDrlFile(drlFilePath);

		PackageBuilder backageBuilder = new PackageBuilder();
		for (Reader r : readers) {
			backageBuilder.addPackageFromDrl(r);
		}
		
		// 检查脚本是否有问题
		if(backageBuilder.hasErrors()) {
			throw new Exception(backageBuilder.getErrors().toString());
		}
		
		return backageBuilder;
	}

	/**
	 * @param drlFilePath 脚本文件路径
	 * @return
	 * @throws FileNotFoundException
	 */
	private List<Reader> readRuleFromDrlFile(List<String> drlFilePath) throws FileNotFoundException {
		if (null == drlFilePath || 0 == drlFilePath.size()) {
			return null;
		}

		List<Reader> readers = new ArrayList<Reader>();

		for (String ruleFilePath : drlFilePath) {
			File file=new File(ruleFilePath);
			readers.add(new FileReader(file));
		}

		return readers;
	}

	/**
	 * 获取测试规则文件
	 * 
	 * @return
	 */
	private List<String> getTestDrlFile() {
		List<String> drlFilePath = new ArrayList<String>();
		URL url=this.getClass().getResource("/");
		drlFilePath
				.add(url.getPath().replaceAll("\\\\", "/")+"addpoint.drl");
		drlFilePath
				.add(url.getPath().replaceAll("\\\\", "/")+"subpoint.drl");

		return drlFilePath;
	}
}

Okay, let's test it:

import java.io.BufferedReader;


public class Test {
	public static void main(String[] args) throws IOException {
		PointRuleEngine pointRuleEngine = new PointRuleEngineImpl();
		while(true){
			InputStream is = System.in;
			BufferedReader br = new BufferedReader(new InputStreamReader(is));
			String input = br.readLine();
			
			if(null != input && "s".equals(input)){
				System.out.println("初始化规则引擎...");
				pointRuleEngine.initEngine();
				System.out.println("初始化规则引擎结束.");
			}else if("e".equals(input)){
				final PointDomain pointDomain = new PointDomain();
				pointDomain.setUserName("hello kity");
				pointDomain.setBackMondy(100d);
				pointDomain.setBuyMoney(500d);
				pointDomain.setBackNums(1);
				pointDomain.setBuyNums(5);
				pointDomain.setBillThisMonth(5);
				pointDomain.setBirthDay(true);
				pointDomain.setPoint(0l);
				
				pointRuleEngine.executeRuleEngine(pointDomain);
				
				System.out.println("执行完毕BillThisMonth:"+pointDomain.getBillThisMonth());
				System.out.println("执行完毕BuyMoney:"+pointDomain.getBuyMoney());
				System.out.println("执行完毕BuyNums:"+pointDomain.getBuyNums());
				System.out.println("执行完毕BackNums:"+pointDomain.getBackNums());
				System.out.println("执行完毕BackMondy:"+pointDomain.getBackMondy());
				System.out.println("执行完毕规则引擎决定发送积分:"+pointDomain.getPoint());
			} else if("r".equals(input)){
				System.out.println("刷新规则文件...");
				pointRuleEngine.refreshEnginRule();
				System.out.println("刷新规则文件结束.");
			}
		}
	}
}


Results of the:

s
初始化规则引擎...
初始化规则引擎结束.

e
增加对hello kity的类型为birthdayPoint的积分操作记录.
增加对hello kity的类型为buyMoneyPoint的积分操作记录.
增加对hello kity的类型为buyNumsPoint的积分操作记录.
增加对hello kity的类型为allFitPoint的积分操作记录.
增加对hello kity的类型为subBackNumsPoint的积分操作记录.
增加对hello kity的类型为subBackMondyPoint的积分操作记录.
执行完毕BillThisMonth:10
执行完毕BuyMoney:1000.0
执行完毕BuyNums:10
执行完毕BackNums:1
执行完毕BackMondy:100.0
执行完毕规则引擎决定发送积分:490
r
刷新规则文件...
刷新规则文件结束.



 The complete project download link: http://download.csdn.net/detail/huxiang19851114/4697914

Guess you like

Origin blog.csdn.net/huxiang19851114/article/details/8123524