解释器模式--派生工单的规则解析与匹配

在目前的项目中,需要定义规则表达式,分析工单对象中的属性的值。


一、需求:

输入规则表达式,和工单对象,运算出结果。例如,规则表达式是 order.office.areaCode.equal(10000),输入参数order后,分析order里面office对象的areaCode属性的值,如果是等于10000,就返回true,否则返回false。


二、设计要求:

1、规则表达式是可以动态编辑的。

2、高扩展性,未来增加containt /  begin_as 等运算符号时,较少改动量

3、可以遍历的获取参数的属性值,例如 order.office.areaCode.……


三、规则表达式的文法,与抽象语法树

1、文法

通过观察可以看到规则表达式的定义分为4部分,包括  “order”、“.office.areaCode”、“equal()”、“10000”。

“order”:输入参数。是不变的,表示输入的参数是一个order对象。

“.office.……”:获取值的操作符。表示要获取order对象里面值,而且可长可短。

“equal()”:比较操作符。表示要进行equal的比较操作。

“10000”:目标值。就是进行比较的目标值。

结果值:这个在表达式上没有显示,但是表示最终获得的结果是一个boolean值。是否符合规则。


2、抽象语法树



四、解决方案

根据以上的表达式的文法,设计了以下几个类:



DeriveRuleInput:输入参数,包括工单对象,与规则表达式

DeriveRuleOutput:输出参数,不同的操作下,返回的结果不一样。获取值的操作符,返回一个对象。比较操作符,返回的是一个boolean值。


AbstractExpression:表达式各个文法的抽象类

TargetVauleExperssion:表示目标值

OperationExpression:获取值的操作符

AbstractComparisonExpression:比较操作符的抽象类,因为以后比较的方式可能需要拓展,需要各种各样的比较方式,除了equal,还要有contain等,因此抽象出来。

CommonComparisonExpression:通用的比较操作符,匹配通用的一些比较操作符。


DeriveRuleHandler:规则计算的处理,接收工单与规则表达式,并返回最终的结果。其中的处理包括,把表达式转化成各个文房抽象类,而且运算起来。


五、具体实现:

1、输入、输出参数的定义

/**
 * 拆分规则解析的输入参数
 * @author hzy
 *
 */
public class DeriveRuleInput {
	
	private IWorkOrder workOrder;
	private String ruleExp;
	public IWorkOrder getWorkOrder() {
		return workOrder;
	}
	public void setWorkOrder(IWorkOrder workOrder) {
		this.workOrder = workOrder;
	}
	public String getRuleExp() {
		return ruleExp;
	}
	public void setRuleExp(String ruleExp) {
		this.ruleExp = ruleExp;
	}
}


/**
 * 规则分析的输出结果
 * @author hzy
 *
 */
public class DeriveRuleOutput {
	
	private Boolean isValid;
	private Object resultObj;
	public Boolean getIsValid() {
		return isValid;
	}
	public void setIsValid(Boolean isValid) {
		this.isValid = isValid;
	}
	public Object getResultObj() {
		return resultObj;
	}
	public void setResultObj(Object resultObj) {
		this.resultObj = resultObj;
	}
}


2、各个文法的抽象与实现

/**
 * 规则表达式的抽象类
 * @author hzy
 *
 */
public abstract class AbstractExpression {
	
	private String expStr;
	
	
	public String getExpStr() {
		return expStr;
	}

	public void setExpStr(String expStr) {
		this.expStr = expStr;
	}

	/**
	 * 表达式的计算
	 * @param input
	 * @return
	 */
	public abstract DeriveRuleOutput interpret(DeriveRuleInput input);
}


/**
 * 比较目标的表达式
 * @author hzy
 *
 */
public class TargetVauleExperssion extends AbstractExpression {

	@Override
	public DeriveRuleOutput interpret(DeriveRuleInput input) {
		DeriveRuleOutput result = new DeriveRuleOutput();
		result.setResultObj(this.getExpStr());
		return result;
	}

}


/**
 * 获取值的操作的表达式
 * @author hzy
 *
 */
public class OperationExpression extends AbstractExpression {
	
	private AbstractExpression previousExp = null;
	private static final Pattern WORD_PATTERN =Pattern.compile("([a-z|A-Z])(\\w*)");

	@Override
	public DeriveRuleOutput interpret(DeriveRuleInput input) {
		
		Object previousResult = null;
		if(this.previousExp != null){ 
			previousResult = this.previousExp.interpret(input).getResultObj();
			
		}else{
			//如果是第一条操作符
			previousResult = input.getWorkOrder();
		}
		
		String methodOfGetOrderValue = "get" + upperFirstLetter(this.getExpStr()); //方法名
		Object vauleFromOrder = null;
		try {
			//通过反射,调用order里面的get方法
			Method method = previousResult.getClass().getMethod(methodOfGetOrderValue);
			vauleFromOrder = method.invoke(previousResult);
			
		} catch (NoSuchMethodException e) {
			throw new RuntimeException("规则表达式中,属性的获取方法不存在["+methodOfGetOrderValue+"]");
			
		} catch (Exception e) {
			//统一在跑出去
			throw new RuntimeException(e);
		}
		
		DeriveRuleOutput result = new DeriveRuleOutput();
		result.setResultObj(vauleFromOrder);
		
		return result;
	}
	
	/**
	 * 使第一个字母大写,如果首单词不是字母就返回原来的字符串,如果里面包含非法字符也返回原字符串。
	 * 例如 字母 abcde--->Abcde,qwe123-->Qwe123,123qwe--->123qwe,qwe%!--->qwe%!
	 * @param word
	 * @return
	 */
	private String upperFirstLetter(String word){
		Matcher matcher = WORD_PATTERN.matcher(word);
		if(!matcher.matches()){ //如果没有匹配,直接返回
			return word;
		}
		
		String firstLetter = matcher.group(1).toUpperCase();
		String restLetter = matcher.group(2);
			
		return firstLetter + "" + restLetter;
	}

	public AbstractExpression getPreviousExp() {
		return previousExp;
	}

	public void setPreviousExp(AbstractExpression previousExp) {
		this.previousExp = previousExp;
	}

}


/**
 * 比较表达式 抽象类
 * @author hzy
 *
 */
public abstract class AbstractComparisonExpression extends AbstractExpression {
	
	protected AbstractExpression previousExp;
	protected AbstractExpression nextExp;
	
	@Override
	public DeriveRuleOutput interpret(DeriveRuleInput input) {
		//上一个表达式得出的值,也就是操作符获取order的属性值
		Object orderValue = this.previousExp.interpret(input).getResultObj();
		
		//下一个表达式得出的值,就需要做比较的目标值
		Object targetValue = null;
		if(this.nextExp != null){
			targetValue = this.nextExp.interpret(input).getResultObj();
		}
		
		boolean isValid = compareOrderToTarget(orderValue, targetValue);
		
		DeriveRuleOutput result = new DeriveRuleOutput();
		result.setIsValid(isValid);
		return result;
	}

	/**
	 * 比较获取的工单的属性值,和目标的值
	 * @param orderValue
	 * @param targetValue
	 * @return
	 */
	protected abstract boolean compareOrderToTarget(Object orderValue, Object targetValue);

	public AbstractExpression getPreviousExp() {
		return previousExp;
	}

	public void setPreviousExp(AbstractExpression previousExp) {
		this.previousExp = previousExp;
	}

	public AbstractExpression getNextExp() {
		return nextExp;
	}

	public void setNextExp(AbstractExpression right) {
		this.nextExp = right;
	}

}

3、通用的比较符的操作实现

/**
 * 通用字符串比较符
 * @author hzy
 *
 */
public class CommonComparisonExpression extends AbstractComparisonExpression {

    private static Logger log = Logger.getLogger(CommonComparisonExpression.class.getName());
	
	public static final String EXC_METHOD_EQUAL = "equal";
	public static final String EXC_METHOD_NO_EQUAL = "no_equl";
	public static final String EXC_METHOD_NO_EXIST = "no_exist";
	public static final String EXC_METHOD_BEGIN_AS = "begin_as";
	public static final String EXC_METHOD_END_AS = "end_as";
	public static final String EXC_METHOD_CONTAINS = "contains";
	public static final String EXC_METHOD_NO_CONTAINS = "no_cont";
	public static final String EXC_METHOD_CONTAINT_KEY = "contain_key";
	
	@Override
	protected boolean compareOrderToTarget(Object orderValue, Object targetValue) {
		String excMethod = this.getExpStr();
		boolean isValid = false;
		
		if(EXC_METHOD_EQUAL.equals(excMethod)){
			isValid = this.excuteEqual(orderValue, targetValue);
		}
		
		if(EXC_METHOD_NO_EQUAL.equals(excMethod)){
			isValid = this.excuteNoEqual(orderValue, targetValue);
		}
		
		if(EXC_METHOD_NO_EXIST.equals(excMethod)){
			isValid = orderValue == null;
		}
		
		if(EXC_METHOD_CONTAINS.equals(excMethod)){
			isValid = this.excuteContains(orderValue, targetValue);
		}
		
		if(EXC_METHOD_NO_CONTAINS.equals(excMethod)){
			isValid = this.excuteNoContains(orderValue, targetValue);
		}
		
		if(EXC_METHOD_BEGIN_AS.equals(excMethod)){
			isValid = this.excuteBeginAs(orderValue, targetValue);
		}
		
		if(EXC_METHOD_END_AS.equals(excMethod)){
			isValid = this.excuteEndAs(orderValue, targetValue);
			
		}
		
		if(EXC_METHOD_CONTAINT_KEY.equals(excMethod)){
			isValid = this.excuteContainKey(orderValue, targetValue);
		}
		
		return isValid;
	}

	private boolean excuteContainKey(Object orderValue, Object targetValue) {
		if(orderValue == null){
			return false;
		}
		if(!(orderValue instanceof Map)){
			log.warning("【规则匹配】从工单获取的["+this.previousExp.getExpStr()+"]的类型是["+orderValue.getClass()+"],没法转换为Map,没法执行contain_key操作,返回false");
			return false;
		}
		
		Map proValueMap = (Map) orderValue;
		return proValueMap.containsKey(targetValue.toString());
	}

	private boolean excuteEndAs(Object orderValue, Object targetValue) {
		if(orderValue == null){
			return false;
		}
		
		if(orderValue instanceof String && targetValue != null){
			return orderValue.toString().endsWith(targetValue.toString());
			
		}else{
			return false;
		}
	}

	private boolean excuteBeginAs(Object orderValue, Object targetValue) {
		if(orderValue == null){
			return false;
		}
		
		if(orderValue instanceof String && targetValue != null){
			return orderValue.toString().startsWith(targetValue.toString());
			
		}else{
			return false;
		}
	}

	private boolean excuteNoContains(Object orderValue, Object targetValue) {
		if(orderValue == null){
			return true;
		}
		
		if(orderValue instanceof String && targetValue != null){
			return !orderValue.toString().contains(targetValue.toString());
			
		}else{
			return false;
		}
	}

	private boolean excuteContains(Object orderValue, Object targetValue) {
		if(orderValue == null){
			return false;
		}
		
		if(orderValue instanceof String && targetValue != null){
			return orderValue.toString().contains(targetValue.toString());
			
		}else{
			return false;
		}
	}

	private boolean excuteNoEqual(Object orderValue, Object targetValue) {
		if(orderValue == null){
			return true;
		}
		return !orderValue.toString().trim().equals(targetValue);
	}

	private boolean excuteEqual(Object orderValue, Object targetValue) {
		if(orderValue == null){
			return false;
		}
		return orderValue.toString().trim().equals(targetValue);
	}
}

4、规则处理器的实现

/**
 * 拆分规则分析的处理器<p/>
 * 主要负责接收输入参数,并且把规则表达式转换成各个组件,并最后运行组件,解析出结果
 * @author hzy
 *
 */
public class DeriveRuleHandler {
	
	private static Logger log = Logger.getLogger(DeriveRuleHandler.class.getName());
	
	public static boolean match(IWorkOrder order, String ruleExp){
		DeriveRuleInput deriveRuleInput = new DeriveRuleInput();
		deriveRuleInput.setRuleExp(ruleExp);
		deriveRuleInput.setWorkOrder(order);
		
		//解析表达式
		List<AbstractExpression> expressionList = parseRuleExp(ruleExp);
		if(expressionList == null || expressionList.size()==0){
			//TODO抛出异常
			log.warning("分析表达式后,没有解析出任何规则,表达式如下:\n"+ ruleExp);
			return false;
		}
		
		//生成抽象语法的树
		AbstractExpression finalTree = buildExpressionTree(expressionList);
		if(finalTree == null){
			log.warning("分析表达式后,没有解析出任何规则,表达式如下:\n"+ ruleExp);
			throw new RuntimeException("分析表达式后,没有解析出任何规则");
		}
		
		//运行计算
		DeriveRuleOutput deriveRuleOutput = finalTree.interpret(deriveRuleInput);
		
		Boolean isValid = deriveRuleOutput.getIsValid();
		if(isValid == null){
			throw new RuntimeException("规则匹配有异常,没有匹配结果,规则如下:\n" + ruleExp);
		}
		return isValid;
	}
	
	/**
	 * 组织各个表达式的运行顺序,并生成语法树
	 * @param expressionList
	 * @return
	 */
	public static AbstractExpression buildExpressionTree(List<AbstractExpression> expressionList){
		
		//声明一个栈对象用于存储抽象语法树
		Stack<AbstractExpression> stack = new Stack<AbstractExpression>();
		
		for(int i=0; i<expressionList.size(); i++){
			AbstractExpression previous = null;
			if(i != 0){
				//把前一次的操作拿出来
				previous = stack.pop();
			}
			
			if(expressionList.get(i) instanceof OperationExpression){
				//如果是获取值的操作符,就要先把前一次的操作拿出来,并设为上一步。
				OperationExpression operateExp = (OperationExpression) expressionList.get(i);
				operateExp.setPreviousExp(previous);
				stack.push(operateExp);
			}
			
			if(expressionList.get(i) instanceof AbstractComparisonExpression){
				//如果是比较值的操作符,就要把前一次的操作拿出来,已经后一次的操作拿出来。然后存到一起做比较
				AbstractComparisonExpression comparisonExp = (AbstractComparisonExpression) expressionList.get(i);
				comparisonExp.setPreviousExp(previous);
				
				if(i+1 <expressionList.size()){ //如果存在下一个操作的表达式
					AbstractExpression next = expressionList.get(i + 1);
					
					if(next instanceof TargetVauleExperssion){ 
						//而且下一个表示式是目标值的表达式
						comparisonExp.setNextExp(next);
					}
				}
				
				stack.push(comparisonExp);
			}
			
			if(expressionList.get(i) instanceof TargetVauleExperssion){
				stack.push(previous);
			}
		}
		
		AbstractExpression finalTree = stack.pop();
		return finalTree;
	}
	
	/**
	 * 解析规则表达式,把表达式的各个部分,解析成对应的Expression
	 * @param ruleExp
	 * @return
	 */
	public static List<AbstractExpression> parseRuleExp(String ruleExp){
		List<AbstractExpression> expressionList = new ArrayList<AbstractExpression>();
		
		int startIndex= 0;
		for(int endIndex=0; endIndex<ruleExp.length(); endIndex++){
			if(ruleExp.charAt(endIndex) == '.' 
					|| ruleExp.charAt(endIndex) == '('
					|| ruleExp.charAt(endIndex) == ')'
					|| endIndex == ruleExp.length()-1){
				
				//把每个两个符号之间的字符提取出来,并做解析
				//例如,order.office.areaCode.equal(10000)
				//截取出 .office. 然后解析出OpreationExpression
				
				String tmp = ruleExp.substring(startIndex, endIndex+1);
				AbstractExpression expression = matchAndCreateExpression(tmp);
				if(expression == null){
					continue;
				}
				
				expressionList.add(expression);
				startIndex = endIndex;
			}
		}
		
		return expressionList;
	}

	/**
	 * 根据表达式的各个字符串,生成Expression
	 * @param ruleExp
	 * @return
	 */
	private static AbstractExpression matchAndCreateExpression(String ruleExp) {
		Matcher matcher = null;
		
		//匹配 获取指令的操作符,如果符合 ".单词.",单词表示通配
		Pattern operationExpressionP = Pattern.compile(".*\\.(\\w+)\\.");
		matcher = operationExpressionP.matcher(ruleExp);
		
		if(matcher.find()){
			AbstractExpression expresson = new OperationExpression();
			expresson.setExpStr(matcher.group(1));
			return expresson;
		}
		
		//匹配比较值的操作符,如果符合 ".单词(",单词表示通配
		Pattern comparisonExpressionP = Pattern.compile(".*\\.(\\w+)\\(");
		matcher = comparisonExpressionP.matcher(ruleExp);
		if(matcher.find()){
			AbstractExpression expresson = new CommonComparisonExpression();
			expresson.setExpStr(matcher.group(1));
			return expresson;
		}
		
		//匹配目标值的操作符,如果符合  "(单词)",单词表示通配
		Pattern targetExpressionP = Pattern.compile("\\(\\s*(.+)\\s*\\)");
		matcher = targetExpressionP.matcher(ruleExp);
		if(matcher.find()){
			AbstractExpression expresson = new TargetVauleExperssion();
			expresson.setExpStr(matcher.group(1));
			return expresson;
		}
		
		//留着待扩展
		
		//都不符合返回空
		return null;
	}
	
}


六、关于修改与拓展

如果以后在应用的过程中,发现匹配错误或失败的情况,只需要找出匹配失败的操作符,并修改改操作符的实现类就可以了。不需要影响系统的其他部分。如果需要添加新的操作符,也是只用实现AbstractExpression就可以了。


举个例,现在来了新的需求,需要添加新的比较方式,对于对象里面Map值,查找里面有没有包含 特定Key,把比较符定义为 contain_key()。

例如,order 里面有一个属性 itemMap,是一个Map,里面存很多参数,如果里面包含一个 Key 为 abc的值,那么就符合规则,返回true。规则为 order.itemMap.contain_key(abc)


由于我已经定义了一个比较符的抽象类 AbstractComparisonExpression,所以直接继承实现就可以。另外,需要处理能识别contain_key 这个语法,所以还要修改处理器DeriveRuleHandler的识别部分,具体代码如下:


/**
 * contain_key的比较操作符的执行
 * @author hzy
 *
 */
public class ContainKeyComparisonExpression extends AbstractComparisonExpression {

	private static Logger log = Logger.getLogger(ContainKeyComparisonExpression.class.getName());
	
	@Override
	protected boolean compareOrderToTarget(Object orderValue, Object targetValue) {
		if(orderValue == null){
			return false;
		}
		if(!(orderValue instanceof Map)){
			log.warning("【规则匹配】从工单获取的["+this.previousExp.getExpStr()+"]的类型是["+orderValue.getClass()+"],没法转换为Map,没法执行contain_key操作,返回false");
			return false;
		}
		
		Map proValueMap = (Map) orderValue;
		return proValueMap.containsKey(targetValue.toString());
	}
}

处理识别部分的修改

//匹配contain_key比较操作符,如果符合 ".contain_key("
		//add by hzy 2014-12-10
		Pattern containtKeyExpression = Pattern.compile("\\.(contain_key)\\(");
		matcher = containtKeyExpression.matcher(ruleExp);
		if (matcher.find()) {
			AbstractExpression expresson = new ContainKeyComparisonExpression();
			expresson.setExpStr(matcher.group(1));
			return expresson;
		}

七、后续

突然发现,这里有问题了,添加新的操作符,是不应该还改其他地方的,如果还有其他改动,说明这里还有问题。最为完善的方式是,添加新的操作符,只需要添加类就行了,不应该到其他地方修改,所以这里就要考虑重构了。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值