项目中遇到了动态配置条件触发相应事件的需求,需要根据String类型的逻辑语句,以及动态获取的数据,计算数据对应的结果,java实现。解决思路及代码实现如下,有需要的同学请自取。


一、需求提取

  根据需求,抛开业务部分,我们可以将需求简化成以下核心逻辑。输入String类型的逻辑字符串,支持的逻辑符号包括 > ,  < , <= ,>= ,== ,() 。 例如: "(a>1&&b<2)||(c>=3&&d==4)" ,动态解析该字符串,并对输入的任意json类数据做出正确的逻辑判断。如{“b” : 10 , "a" : 9 , "c":"error" }。

二、设计思路

  因为每一个最小的逻辑点。如 “a>1” 都只有两个结果:成功或者失败,并且成功或者失败后,往往需要执行下一个逻辑,所以该逻辑模型可以转换成一个二叉树的结构。据此我们先画出  "(a>1&&b<2)||(c>=3&&d==4)"  的逻辑图

  每一个逻辑根据成功或者失败,指向了另外的逻辑,或者指向了最终结果,这里我们可以把指向的这个操作等价成指针,该指针指向了另外一个逻辑实体,或者指向了最终结果,又因为java中的指针,或者说引用都是需要事先指定数据类型的,如果我们使用逻辑实体和布尔类型的两种数据对象,那我们就只能将引用声明为两种数据对象的统一父类Object。但是因为Object在使用过程中涉及到了类型的判断及转化,很不方便,所以我们直接使用逻辑实体表示 逻辑,用 null表示可以直接返回最终结果。

  除了两个引用以外,该逻辑实体应该还包括三个关键字,三个关键字有数据对应的key值"a" , 数据对应的逻辑符号 “>”,数据对应的阈值"1"。

  据此,我们可以确定该逻辑实体的五个字段 ,建出以下实体

 public class Logic {
//值对应的key值
private String key;
//逻辑符号 包括 > < >= <= ==
private double symbol;
//阈值
private double value;
//成功是返回,为null时表示最终结果为true
private Logic sucLogic;
//失败是返回,为null时表示最终结果为false
private Logic failLogic; //后面会用到的两个方法
//在logic树的每一层失败分支子树上都加一个成功时调用的对象
public void setSucLogicEveryFail(Logic logic){
Logic logic1 = this;
while (true){
logic1.setSucLogic( logic );
if ( logic1.getFailLogic != null ){
logic1 = logic1.getFailLogic();
}else {
return;
}
}
}
//在logic树的每一层成功分支上子树上都加一个失败时调用的对象
public void setFailLogicEverySuc(Logic logic){
Logic logic1 = this;
while (true){
logic1.setFailLogic( logic );
if ( logic1.getSucLogic != null ){
logic1 = logic1.getSucLogic ();
}else {
return;
}
}
}
}

  使用该实体的原因如下:

  1) 可以很清楚的表明逻辑关系

  2) 增加了处理时的开销,减少了使用时的开销,更好的支持大批量的数据判断

三、编码实现

  1. 根据string生成Logic对象的代码

 public class CreateLogic {

     private static String[] symbol = {">=","<=",">","<","=="};
private static String[] backSymbol = {"<=",">=","<",">","=="}; public static void main(String[] args) {
CreateLogic createLogic = new CreateLogic();
Logic logic = createLogic.handleContentLogic("(a>1&&b<2)||(c>=3&&d==4)");
System.out.println( logic);
} private Logic handleContentLogic(String content) {
//1.去除掉首位的无效括号
content = this.removeNoUseContent( content );
//2.将content拆成小的逻辑块。
List<String> blockContents = new ArrayList<>();
int point = 0;
int flag = 0;
for (int i = 0; i < content.length(); i++) {
char c = content.charAt(i);
if( '(' == c){
flag++;
continue;
}else if( ')' == c){
flag--;
if( flag == 0 ){
blockContents.add( content.substring( point , i + 1) );
point = i + 1;
}
}else if( flag == 0 && ('|' == content.charAt(i) || '&' == content.charAt(i)) ){
if( i - point > 1){
blockContents.add( content.substring( point , i ) );
point = i;
}
}else if( i == content.length() - 1){
blockContents.add( content.substring( point , i + 1 ) );
}
}
//3.遍历获取最终逻辑
Logic logic = null;
for (int i = 0; i < blockContents.size(); i++) {
String blockContent = blockContents.get(i);
if( blockContent.startsWith("||(") ){
Logic logic1 = this.handleContentLogic(blockContent.substring(2));
logic.setFailLogicEverySuc(logic1);
}else if( blockContent.startsWith("&&(") ){
Logic logic1 = this.handleContentLogic(blockContent.substring(2));
logic.setSucLogicEveryFail(logic1);
}else if( blockContent.startsWith("&&") ) {
Logic logic1 = this.getLogicBySimpleContent(blockContent.substring(2));
logic1.setSucLogicEveryFail(logic);
logic = logic1;
}else if( blockContent.startsWith("||") ) {
Logic logic1 = this.getLogicBySimpleContent(blockContent.substring(2));
logic1.setFailLogicEverySuc(logic);
logic = logic1;
}else {
logic = this.getLogicBySimpleContent(blockContent);
}
}
return logic;
} /**
* 去除掉首位的无效括号
* @param content
* @return
*/
public String removeNoUseContent( String content ){
List<String> list = new ArrayList<>(Arrays.asList(content.split(""))) ;
//1.首位的小括号为无效的小括号,先去除掉
int flag1 = 0;
int flag2 = 0;
while (true){
if( "(".equals(list.get(0) )){
flag1++;
list.remove(0);
}else {
break;
}
}
if( flag1 > 0 ){
for (int i = 0; i < list.size(); i++) {
if( flag1 == 0 ){
break;
}
if( "(".equals(list.get(i) ) ){
flag2++;
}else if( ")".equals( list.get(i) ) ){
if(flag2 > 0){
flag2--;
continue;
}else {
flag1--;
list.remove(i);
i--;
}
}
}
}
return StringUtils.join(list.toArray());
} /**
* 简单的逻辑文本直接转换成一个逻辑实体
* @param blockContent
* @return
*/
private Logic getLogicBySimpleContent(String blockContent) {
Logic logic = new Logic();
for (int i = 0; i < symbol.length; i++) {
if( blockContent.indexOf( symbol[i] ) != -1 ){
String value1 = blockContent.substring(0 , blockContent.indexOf( symbol[i] ));
String value2 = blockContent.substring( blockContent.indexOf( symbol[i] ) + symbol[i].length());
try {
double b = Double.valueOf(value2);
logic.setKey(value1);
logic.setValue(b);
logic.setSymbol(symbol[i]);
}catch (Exception e){
double b = Double.valueOf(value1);
logic.setKey(value2);
logic.setValue(b);
logic.setSymbol(backSymbol[i]);
}
return logic;
}
}
return logic;
}
}

  2. 根据Logic和json判断最终结果

  public class HandleLogic {
/**
* 根据逻辑树,递归获取最终的逻辑结果s
*/
public boolean handleMessageByLogicCore(Logic logic , JSONObject object ) {
boolean bool = false;
String key = logic.getKey();
if( object.get(key) == null ){
return this.getLogicByResult(logic , bool , object);
}
double value = logic.getValue();
double realValue = object.getDoubleValue( key );
switch ( logic.getSymbol() ){
case ">=":
bool = realValue >= value;
break;
case "<=":
bool = realValue <= value;
break;
case "==":
bool = realValue == value;
break;
case "<":
bool = realValue < value;
break;
case ">":
bool = realValue > value;
break;
}
return this.getLogicByResult(logic , bool , object);
} /**
* 根据逻辑的结果,获取逻辑的成功/失败的子逻辑树,不存在则直接返回成功/失败
* @param logic 当前逻辑树
* @param b 当前逻辑树的执行结果
* @param object 当前逻辑树的处理对象
* @return
*/
private boolean getLogicByResult(Logic logic, boolean b, JSONObject object) {
if( b ){
if( logic.getSucLogic() == null ){
return true;
}else {
return handleMessageByLogicCore( logic.getSucLogic() , object );
}
}else {
if( logic.getFailLogic() == null ){
return false;
}else {
return handleMessageByLogicCore( logic.getFailLogic() , object );
}
}
}
}

  以上就是逻辑语句编译的总结,原创不易,转载请注明出处。

(JAVA)String类型的逻辑语句编译的更多相关文章

  1. java string类型的初始化

    以下基本上是java string类型最常用的三种方法 new string()就不介绍了  基本等同于第三种 String a;  申明一个string类型的 a,即没有在申请内存地址,更没有在内存 ...

  2. Java——string类型与date类型之间的转化

    String类型转化为Date类型 方法一 Date date=new Date("2019-01-25"); 方法二 String =(new SimpleDateFormat( ...

  3. JAVA String类型和原型模式

    如上例所述,变量a,b和它们的值10,20都是存在栈里面,声明的所以String类型的引用也都是存在栈里.而字符串abc是存在字符串常量池中,new出来的String对象则是存在堆里. String ...

  4. Java String类型数据的字节长度

    问题描述: 向Oracle数据库中一varchar2(64)类型字段中插入一条String类型数据,程序使用String.length()来进行数据的长度校验,如果数据是纯英文,没有问题,但是如果数据 ...

  5. JAVA String类型的一些小操作

    String类型是否包含某个String类型的函数:源字符串.contains(包含字符串)  返回值为:boolean类型(true或false) String类型把某个字符替换成另一个字符:源字符 ...

  6. java String 类型总结

    java中String是个对象,是引用类型?,基础类型与引用类型的区别是,基础类型只表示简单的字符或数字,引用类型可以是任何复杂的数据结构,基本类型仅表示简单的数据类型,引用类型可以表示复杂的数据类型 ...

  7. 为什么说Java String 类型的值是不可改变的?

    String对象是不可变的,它的内容是不能改变的.下列代码会改变字符串的内容吗? 1 2 String s = "Java"; s = "HTML"; 答案是不 ...

  8. java String类型转 java.sql.time类型

    String[] timePhase = reservationRuleInDTO.getTimePhase().split(",");List<ReservationTim ...

  9. Java String的相关性质分析

    引言 String可以说是在Java开发中必不可缺的一种类,String容易忽略的细节也很多,对String的了解程度也反映了一个Java程序员的基本功.下面就由一个面试题来引出对String的剖析. ...

随机推荐

  1. App开发流程之iOS开发证书拾遗

    很久没有总结,回头看了一下过期的账号,记录的内容少之又少.最近有一些时间,想好好总结记录一下. 由于很久没有记录,想写的东西很多又很杂,想了一下,一篇一篇罗列知识点和经验,还不如写一个系列,记录一个应 ...

  2. codevs1281 Xn数列

    题目描述 Description 给你6个数,m, a, c, x0, n, g Xn+1 = ( aXn + c ) mod m,求Xn m, a, c, x0, n, g<=10^18 输入 ...

  3. C++ STL中Map的按Key排序跟按Value排序

    C++ STL中Map的按Key排序和按Value排序 map是用来存放<key, value>键值对的数据结构,可以很方便快速的根据key查到相应的value.假如存储学生和其成绩(假定 ...

  4. 文本去重之MinHash算法——就是多个hash函数对items计算特征值,然后取最小的计算相似度

    来源:http://my.oschina.net/pathenon/blog/65210 1.概述     跟SimHash一样,MinHash也是LSH的一种,可以用来快速估算两个集合的相似度.Mi ...

  5. Python-单元测试unittest

    Python中有一个自带的单元测试框架是unittest模块,用它来做单元测试,它里面封装好了一些校验返回的结果方法和一些用例执行前的初始化操作,概念见下: TestCase 也就是测试用例 Test ...

  6. Hexo 版本

    Mac hexo s 启动Hexo服务报错如下: Error: The module '/usr/local/lib/node_modules/hexo-cli/node_modules/.0.8.0 ...

  7. Gym - 101196:F Removal Game(区间DP)

    题意:一个环状数组,给定可以删去一个数,代价的相邻两个数的gcd,求最小代价. 思路:区间DP即可,dp[i][j]表示[i,j]区间只剩下i和j时的最小代价,那么dp[i][j]=min  dp[i ...

  8. ACM学习历程—Hihocoder 1290 Demo Day(动态规划)

    http://hihocoder.com/problemset/problem/1290 这题是这次微软笔试的第三题,过的人比第一题少一点,这题一眼看过去就是动态规划,不过转移方程貌似不是很简单,调试 ...

  9. 2017.10.1北京清北综合强化班DAY1

    a[问题描述]你是能看到第一题的 friends 呢.——hja何大爷对字符串十分有研究,于是天天出字符串题虐杀 zhx. 何大爷今天为字符串定义了新的权值计算方法.一个字符串 由小写字母组成,字符串 ...

  10. C/C++文件读写操作总结

    本文主要从两方面介绍读写文件操作,一个是C,另一个是C++. 一.基于C的文件操作. 在ANSI C中对文件操作有两种方式,一种是流式文件操作,另一种是I/O文件操作.下面分别介绍. 1.流式文件操作 ...