基于Java實(shí)現(xiàn)一個(gè)復(fù)雜關(guān)系表達(dá)式過(guò)濾器
背景
最近,有一個(gè)新需求,需要后臺(tái)設(shè)置一個(gè)復(fù)雜的關(guān)系表達(dá)式,根據(jù)用戶指定ID,解析該用用戶是否滿足該條件,后臺(tái)設(shè)置類似于禪道的搜索條件

但是不同的是禪道有且僅有兩個(gè)組,每個(gè)組最多三個(gè)條件
而我們這邊組與關(guān)系可能是更復(fù)雜的,組中有組,每個(gè)條件都是有且或關(guān)系的。由于保密原因,原型就不發(fā)出來(lái)了。
看到這個(gè)需求,作為一個(gè)后端,第一時(shí)間想到的是類似QLEpress這類的表達(dá)式框架,只要構(gòu)建一個(gè)表達(dá)式,通過(guò)解析表達(dá)式即可快速對(duì)目標(biāo)用戶進(jìn)行篩選,但是可惜的是前端同學(xué)不干了,因?yàn)樽鳛槭褂胿ue或react這類數(shù)據(jù)驅(qū)動(dòng)的框架來(lái)說(shuō),將表達(dá)式轉(zhuǎn)換為為上述的一個(gè)表單太難了 ,所以想了一下,決定自己定義一個(gè)數(shù)據(jù)結(jié)構(gòu),實(shí)現(xiàn)表達(dá)式解析。方便前端同學(xué)的處理。
分析準(zhǔn)備
雖然是用類實(shí)現(xiàn)表達(dá)式,但是其本質(zhì)上依舊還是個(gè)表達(dá)式,我們列舉一個(gè)簡(jiǎn)單的表達(dá)式:設(shè)條件為a,b,c,d,我們隨意構(gòu)造一個(gè)表達(dá)式:
boolean result=a>100 && b=10 || (c != 3 && d < 50)
我們對(duì)表達(dá)式進(jìn)行分析,可以發(fā)現(xiàn)表達(dá)式 都是共同屬性有:
過(guò)濾字段(a、b、c、d),判斷條件(大于、小于、不等于等等),對(duì)比值(a>100 中的100)。
另外,還有關(guān)聯(lián)關(guān)系(且、或)和計(jì)算優(yōu)先級(jí)這幾個(gè)屬性組成。
于是我們對(duì)表達(dá)式進(jìn)行簡(jiǎn)化:
令a>100 =>A,b=10 =>B,c!=3=>C ,d<50=>D,于是我們得到:
result=A && B || (C && D)
現(xiàn)在問(wèn)題來(lái)了,如何處理優(yōu)先級(jí)呢?
如上表達(dá)式,很明顯,這是一個(gè)大學(xué)里學(xué)過(guò)的標(biāo)準(zhǔn)的中序表達(dá)式,于是,我們畫(huà)一下它的樹(shù)形圖:

根據(jù)這個(gè)圖,我們可以明顯的看到,A且B 和C且D是同一級(jí)別,于是,我們按照這個(gè)理論設(shè)計(jì)一個(gè)層級(jí)的概念Deep,我們標(biāo)注一下,然后再對(duì)節(jié)點(diǎn)的類型做一下區(qū)分,可得:

我們可以看到作為葉子節(jié)點(diǎn)(上圖綠色部分),相對(duì)于其計(jì)算計(jì)算關(guān)系,遇到了一定是優(yōu)先計(jì)算的,所以對(duì)于深度的優(yōu)先級(jí),我們僅需要考慮非葉子節(jié)點(diǎn)即可,即上圖中的藍(lán)色節(jié)點(diǎn)部分,于是我們得到了,計(jì)算優(yōu)先級(jí)這個(gè)概念我們可以轉(zhuǎn)換為表達(dá)式的深度。
我們?cè)倏瓷厦孢@個(gè)圖,Deep1 的關(guān)系是Deep2中 A且B 和 C且D兩個(gè)表達(dá)式計(jì)算出的結(jié)果再進(jìn)行與或關(guān)系的,我們?cè)O(shè)A 且B 為 G1, C且D為 G2,于是我們發(fā)現(xiàn)關(guān)系節(jié)點(diǎn)關(guān)聯(lián)的類型有兩種類型,一種是條件Condition ,一種是組Group

至此,這個(gè)類的雛形基本就確定了。這個(gè)類包含 關(guān)聯(lián)關(guān)系(Relation)、判斷字段(Field)、運(yùn)算符(Operator)、運(yùn)算值(Values)、類型(Type)、深度(Deep)
但是,有個(gè)問(wèn)題,上面的分析中,我們?cè)趯⒈磉_(dá)式轉(zhuǎn)換成樹(shù),現(xiàn)在我們?cè)囍鴮⑵溥€原,于是我們一眼可以得到其中一種表達(dá)式:
result=(A && B)||(C && D)
很顯然,和我們的原來(lái)的表達(dá)式并不一致,這是因?yàn)槲覀兩鲜鰞H能記錄表達(dá)式的計(jì)算順序,而不能完全準(zhǔn)確的表示這個(gè)表達(dá)式,這是因?yàn)樵谖覀兘馕霰磉_(dá)式的過(guò)程中,不僅是有深度、還有一個(gè)時(shí)序關(guān)系,即從左到右的順序表示,而此時(shí)G1中的內(nèi)容實(shí)際上在原表達(dá)式中的深度應(yīng)該是1而不是2,然后我們引入序號(hào)的概念,將原來(lái)樹(shù)變成有向的圖即:

根據(jù)這個(gè)圖,我們就還原出有且唯一的一個(gè)表達(dá)式了:result= A && B ||(C && D)。
好了,我們分析了半天,原理說(shuō)完了,回到最初始的問(wèn)題:前后端怎么實(shí)現(xiàn)?對(duì)著上圖想象一下,貌似還是無(wú)法處理,因?yàn)檫@個(gè)結(jié)構(gòu)還是太復(fù)雜了。對(duì)于前端,數(shù)據(jù)最好是方便遍歷的,對(duì)于后端,數(shù)據(jù)最好是方便處理的,于是這時(shí)候我們需要將上面這個(gè)圖轉(zhuǎn)換成一個(gè)數(shù)組。
實(shí)現(xiàn)方式
上面說(shuō)到了需要一個(gè)數(shù)組的結(jié)構(gòu),我們具體分析一下這個(gè)部分

我們發(fā)現(xiàn)作為葉子節(jié)點(diǎn),可以始終優(yōu)先計(jì)算,所以我們可以將其壓縮,并將關(guān)系放置在其中一個(gè)表達(dá)式中形成 ^A -> &&B或 A&& -> B$ 的形式,這里我用正則的開(kāi)始(^) 和結(jié)束($) 表示了一下開(kāi)始 和 結(jié)束 的概念,這里為了與產(chǎn)品原型保持一致我們用第一種方式,即關(guān)系符號(hào)表示與前一個(gè)元素的關(guān)系,于是我們?cè)俜治鲆幌拢?/p>

再對(duì)序號(hào)進(jìn)行改造:

于是我們得到最終的數(shù)據(jù)結(jié)構(gòu):
@Data
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = true)
public class ExpressDto {
/**
* 序號(hào)
*/
private Integer seq;
/**
* 深度(運(yùn)算優(yōu)先級(jí))
*/
private Integer deep;
/**
* 關(guān)系運(yùn)算符
*/
private String relation;
/**
* 類型
*/
private String type;
/**
* 運(yùn)算條件
*/
private String field;
/**
* 邏輯運(yùn)算符
*/
private String operator;
/**
* 運(yùn)算值
*/
private String values;
/**
* 運(yùn)算結(jié)果
*/
private Boolean result;
}現(xiàn)在數(shù)據(jù)結(jié)構(gòu)終于完成,既方便存儲(chǔ),又(相對(duì))方便前臺(tái)展示,現(xiàn)在構(gòu)造一個(gè)稍微復(fù)雜的表達(dá)式
A &&(( B || C )|| (D && E)) && F
換成數(shù)組對(duì)象,開(kāi)始用BEGIN標(biāo)識(shí),表達(dá)式類型用CONDITION表示,組用GROUP表示。
[
{"seq":1,"deep":1,relation:"BEGIN","type":"CONDITION","field"="A"...},
{"seq":2,"deep":1,relation:"AND","type":"GROUP","field":""...},
{"seq":3,"deep":2,relation:"BEGIN","type":"GROUP","field":""...},
{"seq":4,"deep":3,relation:"BEGIN","type":"CONDITION","field":"B"...},
{"seq":5,"deep":3,relation:"OR","type":"CONDITION","field":"C"...},
{"seq":6,"deep":2,relation:"OR","type":"GROUP","field":""...},
{"seq":7,"deep":3,relation:"BEGIN","type":"CONDITION","field":"D"...},
{"seq":8,"deep":3,relation:"AND","type":"CONDITION","field":"E"...},
{"seq":9,"deep":1,relation:"AND","type":"CONDITION","field":"F"...}
]現(xiàn)在就剩最后一個(gè)問(wèn)題:如何通過(guò)這個(gè)json對(duì)數(shù)據(jù)進(jìn)行過(guò)濾了
由于數(shù)組對(duì)象的本質(zhì)依舊是一個(gè)中綴表達(dá)式,所以其本質(zhì)依舊是一個(gè)中綴表達(dá)式的解析,關(guān)于解析原理,這里不多介紹,簡(jiǎn)單的說(shuō)就是通過(guò)數(shù)據(jù)棧和符號(hào)棧根據(jù)括號(hào)(在我們這里稱為組)進(jìn)行遍歷,想了解更多,可以通過(guò)下面這篇文章復(fù)習(xí)一下
于是我們定義三個(gè)變量:
//關(guān)系 棧 Deque<String> relationStack=new LinkedList(); //結(jié)果棧 Deque<Boolean> resultStack=new LinkedList(); // 當(dāng)前深度 Integer nowDeep=1;
通過(guò)遍歷數(shù)組,將關(guān)系與結(jié)果入棧,當(dāng)發(fā)現(xiàn)需要優(yōu)先計(jì)算的時(shí)候,從結(jié)果棧中取出兩個(gè)值,從關(guān)系棧中取出關(guān)系運(yùn)算符,計(jì)算后再次入棧,等待下一次計(jì)算
for (ExpressDto expressDto:list) {
if(!StringUtils.equals(expressDto.getType(),"GROUP")){
//TODO 進(jìn)行具體單個(gè)表達(dá)式計(jì)算并獲取結(jié)果
resultStack.push(expressDto.getResult());
// 將關(guān)系放入棧中
relationStack.push(expressDto.getRelation());
if(deep==0 && resultStack.size()>1){ //由于已處理小于0的deep,當(dāng)前deep理論上是>=0的,0表示同等級(jí),需要立即運(yùn)算
relationOperator(relationStack, resultStack);
}
}else{
// 將關(guān)系放入棧中
relationStack.push(expressDto.getRelation());
}
}
?
private void relationOperator(Deque<String> relationStack, Deque<Boolean> resultStack) {
Boolean lastResult= resultStack.pop();
Boolean firstResult= resultStack.pop();
String relation=relationStack.pop();
if(StringUtils.equals(relation,"AND")){
resultStack.push(firstResult&& lastResult) ;
return;
}
if(StringUtils.equals(relation,"OR")){
resultStack.push( firstResult|| lastResult);
return;
}else{
throw new RuntimeException("表達(dá)式解析異常:關(guān)系表達(dá)式錯(cuò)誤");
}
}再說(shuō)一下注意的邊界事項(xiàng):
1.首先我們同級(jí)中關(guān)聯(lián)關(guān)系僅存在且、或兩種,而這兩種的計(jì)算優(yōu)先級(jí)是一樣的。故同一個(gè)Deep下,從左到右依次遍歷計(jì)算即可。
2.當(dāng)遇到GROUP的類型時(shí),相當(dāng)于遇到了"(",我們可以發(fā)現(xiàn)它后面的元素Deep +1 直到Deep -1為止")"結(jié)束,而括號(hào)中的元素需要優(yōu)先計(jì)算,也就是說(shuō)"()"所產(chǎn)生優(yōu)先級(jí)通過(guò)Deep 和Type=GROUP 共同控制
3.當(dāng)Deep減少時(shí),意味著遇到了")",此時(shí)結(jié)束的Group的數(shù)量等于Deep減少的數(shù)量,針對(duì)")"結(jié)束,每遇到一個(gè)")" 都需要對(duì)該級(jí)括號(hào)進(jìn)行檢查,是否同級(jí)別的元素是否已經(jīng)計(jì)算完畢。
/**
* 處理層級(jí)遺留元素
*
* @param relationStack
* @param resultStack
*/
private void computeBeforeEndGroup(Deque<String> relationStack, Deque<Boolean> resultStack) {
boolean isBeginSymbol=StringUtils.equals(relationStack.peek(),"BEGIN");//防止group中僅有一個(gè)判斷條件
while(!isBeginSymbol){//上一個(gè)運(yùn)算符非BEGIN,說(shuō)明該group中還有運(yùn)算需要優(yōu)先處理,正常這里應(yīng)該僅循環(huán)一次
relationOperator(relationStack, resultStack);
isBeginSymbol=StringUtils.equals(relationStack.peek(),"BEGIN");
}
if(isBeginSymbol){
relationStack.pop();//該優(yōu)先級(jí)處理完畢,將BEGIN運(yùn)算符彈出
}
}4.當(dāng)遍歷結(jié)束發(fā)現(xiàn)最后一個(gè)元素Deep不等于1時(shí),意味著有括號(hào)結(jié)束,這時(shí),同樣需要進(jìn)行括號(hào)結(jié)束處理
最后上完整代碼:
/**
* 表達(dá)式解析器
* 表達(dá)式規(guī)則:
* 關(guān)系relation屬性有:BEGIN、AND、OR 三種
* 表達(dá)式類型 Type 屬性有:GROUP、CONDITION 兩種
* 深度 deep 屬性 根節(jié)點(diǎn)為 1,每增加一個(gè)括號(hào)(GROUP)deep+1,括號(hào)結(jié)束deep-1
* 序號(hào)req:初始值為1,往后依次遞增,用于防止表達(dá)式解析順序錯(cuò)誤
* exp1:表達(dá)式:A &&(( B || C )|| (D && E)) && F
* 分解對(duì)象:
* [
* {"seq":1,"deep":1,relation:"BEGIN","type":"CONDITION","field"="A"...},
* {"seq":2,"deep":1,relation:"AND","type":"GROUP","field":""...},
* {"seq":3,"deep":2,relation:"BEGIN","type":"GROUP","field":""...},
* {"seq":4,"deep":3,relation:"BEGIN","type":"CONDITION","field":"B"...},
* {"seq":5,"deep":3,relation:"OR","type":"CONDITION","field":"C"...},
* {"seq":6,"deep":2,relation:"OR","type":"GROUP","field":""...},
* {"seq":7,"deep":3,relation:"BEGIN","type":"CONDITION","field":"D"...},
* {"seq":8,"deep":3,relation:"AND","type":"CONDITION","field":"E"...},
* {"seq":9,"deep":1,relation:"AND","type":"CONDITION","field":"F"...}
* ]
*
* exp2:(A || B && C)||(D && E && F)
* [
* {"seq":1,"deep":1,relation:"BEGIN","type":"GROUP","field":""...},
* {"seq":2,"deep":2,relation:"BEGIN","type":"CONDITION","field":"A"...},
* {"seq":3,"deep":2,relation:"OR","type":"CONDITION","field":"B"...},
* {"seq":4,"deep":2,relation:"AND","type":"CONDITION","field":"C"...},
* {"seq":5,"deep":1,relation:"OR","type":"GROUP","field":""...},
* {"seq":6,"deep":2,relation:"BEGIN","type":"CONDITION","field":"D"...},
* {"seq":7,"deep":2,relation:"AND","type":"CONDITION","field":"E"...},
* {"seq":8,"deep":2,relation:"AND","type":"CONDITION","field":"F"...}
* ]
*
*
* @param list
* @return
*/
public boolean expressProcessor(List<ExpressDto>list){
//關(guān)系 棧
Deque<String> relationStack=new LinkedList();
//結(jié)果棧
Deque<Boolean> resultStack=new LinkedList();
// 當(dāng)前深度
Integer nowDeep=1;
Integer seq=0;
for (ExpressDto expressDto:list) {
// 順序檢測(cè),防止順序錯(cuò)誤
int checkReq=expressDto.getSeq()-seq;
if(checkReq!=1){
throw new RuntimeException("表達(dá)式異常:解析順序異常");
}
seq=expressDto.getSeq();
//計(jì)算深度(計(jì)算優(yōu)先級(jí)),判斷當(dāng)前邏輯是否需要處理括號(hào)
int deep=expressDto.getDeep()-nowDeep;
// 賦予當(dāng)前深度
nowDeep=expressDto.getDeep();
//deep 減小,說(shuō)明有括號(hào)結(jié)束,需要處理括號(hào)到對(duì)應(yīng)的層級(jí),deep減少數(shù)量等于組(")")結(jié)束的數(shù)量
while(deep++ < 0){
computeBeforeEndGroup(relationStack, resultStack);
}
?
if(!StringUtils.equals(expressDto.getType(),"GROUP")){
//TODO 進(jìn)行具體單個(gè)表達(dá)式計(jì)算并獲取結(jié)果
resultStack.push(expressDto.getResult());
// 將關(guān)系放入棧中
relationStack.push(expressDto.getRelation());
if(deep==0 && resultStack.size()>1){ //由于已處理小于0的deep,當(dāng)前deep理論上是>=0的,0表示同等級(jí),需要立即運(yùn)算
relationOperator(relationStack, resultStack);
}
}else{
// 將關(guān)系放入棧中
relationStack.push(expressDto.getRelation());
}
}
//遍歷完畢,處理?xiàng)V形催M(jìn)行運(yùn)算的節(jié)點(diǎn)
while(nowDeep-- > 0){ // 這里使用 nowdeep>0 的原因是最后deep=1的關(guān)系表達(dá)式也需要進(jìn)行處理
computeBeforeEndGroup(relationStack, resultStack);
}
if(resultStack.size()!=1){
throw new RuntimeException("表達(dá)式解析異常:解析結(jié)果數(shù)量異常解析數(shù)量:"+resultStack.size());
}
return resultStack.pop();
}
?
/**
* 處理層級(jí)遺留元素
*
* @param relationStack
* @param resultStack
*/
private void computeBeforeEndGroup(Deque<String> relationStack, Deque<Boolean> resultStack) {
boolean isBeginSymbol=StringUtils.equals(relationStack.peek(),"BEGIN");//防止group中僅有一個(gè)判斷條件
while(!isBeginSymbol){//上一個(gè)運(yùn)算符非BEGIN,說(shuō)明該group中還有運(yùn)算需要優(yōu)先處理,正常這里應(yīng)該僅循環(huán)一次
relationOperator(relationStack, resultStack);
isBeginSymbol=StringUtils.equals(relationStack.peek(),"BEGIN");
}
if(isBeginSymbol){
relationStack.pop();//該優(yōu)先級(jí)處理完畢,將BEGIN運(yùn)算符彈出
}
}
?
?
/**
* 關(guān)系運(yùn)算處理
* @param relationStack
* @param resultStack
*/
private void relationOperator(Deque<String> relationStack, Deque<Boolean> resultStack) {
Boolean lastResult= resultStack.pop();
Boolean firstResult= resultStack.pop();
String relation=relationStack.pop();
if(StringUtils.equals(relation,"AND")){
resultStack.push(firstResult&& lastResult) ;
return;
}
if(StringUtils.equals(relation,"OR")){
resultStack.push( firstResult|| lastResult);
return;
}else{
throw new RuntimeException("表達(dá)式解析異常:關(guān)系表達(dá)式錯(cuò)誤");
}
}簡(jiǎn)單寫(xiě)了幾個(gè)測(cè)試用例:
/**
* 表達(dá)式:A
*/
@Test
public void expTest0(){
ExpressDto E1=new ExpressDto().setDeep(1).setResult(false).setSeq(1).setType("CONDITION").setField("A").setRelation("BEGIN");
List<ExpressDto> list = new ArrayList();
list.add(E1);
boolean re=expressProcessor(list);
Assertions.assertFalse(re);
}
/**
* 表達(dá)式:(A && B)||(C || D)
*/
@Test
public void expTest1(){
ExpressDto E1=new ExpressDto().setDeep(1).setSeq(1).setType("GROUP").setRelation("BEGIN");
ExpressDto E2=new ExpressDto().setDeep(2).setResult(true).setSeq(2).setType("Condition").setField("A").setRelation("BEGIN");
ExpressDto E3=new ExpressDto().setDeep(2).setResult(false).setSeq(3).setType("Condition").setField("B").setRelation("AND");
ExpressDto E4=new ExpressDto().setDeep(1).setSeq(4).setType("GROUP").setRelation("OR");
ExpressDto E5=new ExpressDto().setDeep(2).setResult(true).setSeq(5).setType("Condition").setField("C").setRelation("BEGIN");
ExpressDto E6=new ExpressDto().setDeep(2).setResult(false).setSeq(6).setType("Condition").setField("D").setRelation("OR");
?
List<ExpressDto> list = new ArrayList();
list.add(E1);
list.add(E2);
list.add(E3);
list.add(E4);
list.add(E5);
list.add(E6);
boolean re=expressProcessor(list);
Assertions.assertTrue(re);
}
?
/**
* 表達(dá)式:A && (B || C && D)
*/
@Test
public void expTest2(){
ExpressDto E1=new ExpressDto().setDeep(1).setResult(true).setSeq(1).setType("Condition").setField("A").setRelation("BEGIN");
ExpressDto E2=new ExpressDto().setDeep(1).setSeq(2).setType("GROUP").setRelation("AND");
ExpressDto E3=new ExpressDto().setDeep(2).setResult(false).setSeq(3).setType("Condition").setField("B").setRelation("BEGIN");
ExpressDto E4=new ExpressDto().setDeep(2).setResult(false).setSeq(4).setType("Condition").setField("C").setRelation("OR");
ExpressDto E5=new ExpressDto().setDeep(2).setResult(true).setSeq(5).setType("Condition").setField("D").setRelation("AND");
List<ExpressDto> list = new ArrayList();
list.add(E1);
list.add(E2);
list.add(E3);
list.add(E4);
list.add(E5);
boolean re=expressProcessor(list);
Assertions.assertFalse(re);
E4.setResult(true);
list.set(3,E4);
re=expressProcessor(list);
Assertions.assertTrue(re);
E1.setResult(false);
list.set(0,E1);
re=expressProcessor(list);
Assertions.assertFalse(re);
}
?
@Test
public void expTest3(){
ExpressDto E1=new ExpressDto().setDeep(1).setResult(true).setSeq(1).setType("Condition").setField("A").setRelation("BEGIN");
ExpressDto E2=new ExpressDto().setDeep(1).setSeq(2).setType("GROUP").setRelation("OR");
ExpressDto E3=new ExpressDto().setDeep(2).setResult(true).setSeq(3).setType("Condition").setField("B").setRelation("BEGIN");
ExpressDto E4=new ExpressDto().setDeep(2).setSeq(4).setType("GROUP").setRelation("AND");
ExpressDto E5=new ExpressDto().setDeep(3).setResult(true).setSeq(5).setType("Condition").setField("C").setRelation("BEGIN");
ExpressDto E6=new ExpressDto().setDeep(3).setResult(false).setSeq(6).setType("Condition").setField("D").setRelation("OR");
?
List<ExpressDto> list = new ArrayList();
list.add(E1);
list.add(E2);
list.add(E3);
list.add(E4);
list.add(E5);
list.add(E6);
boolean re=expressProcessor(list);
Assertions.assertTrue(re);
}
?
/**
* 表達(dá)式:A &&(( B || C )|| (D && E))
*/
@Test
public void expTest4(){
ExpressDto E1=new ExpressDto().setDeep(1).setSeq(1).setType("CONDITION").setResult(true).setField("A").setRelation("BEGIN");
ExpressDto E2=new ExpressDto().setDeep(1).setSeq(2).setType("GROUP").setRelation("AND");
ExpressDto E3=new ExpressDto().setDeep(2).setSeq(3).setType("GROUP").setRelation("BEGIN");
ExpressDto E4=new ExpressDto().setDeep(3).setSeq(4).setType("CONDITION").setResult(true).setField("B").setRelation("BEGIN");
ExpressDto E5=new ExpressDto().setDeep(3).setSeq(5).setType("CONDITION").setResult(true).setField("C").setRelation("OR");
ExpressDto E6=new ExpressDto().setDeep(2).setSeq(6).setType("GROUP").setRelation("OR");
ExpressDto E7=new ExpressDto().setDeep(3).setSeq(7).setType("CONDITION").setResult(false).setField("D").setRelation("BEGIN");
ExpressDto E8=new ExpressDto().setDeep(3).setSeq(8).setType("CONDITION").setResult(false).setField("E").setRelation("AND");
List<ExpressDto> list = new ArrayList();
list.add(E1);
list.add(E2);
list.add(E3);
list.add(E4);
list.add(E5);
list.add(E6);
list.add(E7);
list.add(E8);
boolean re=expressProcessor(list);
Assertions.assertTrue(re);
}
?
?
/**
* 表達(dá)式:(A)
*/
@Test
public void expTest5(){
ExpressDto E1=new ExpressDto().setDeep(1).setSeq(1).setType("GROUP").setRelation("BEGIN");
ExpressDto E2=new ExpressDto().setDeep(2).setResult(true).setSeq(2).setType("Condition").setField("A").setRelation("BEGIN");
List<ExpressDto> list = new ArrayList();
list.add(E1);
list.add(E2);
boolean re=expressProcessor(list);
Assertions.assertTrue(re);
E2.setResult(false);
list.set(1,E2);
Assertions.assertFalse(expressProcessor(list));
}測(cè)試結(jié)果:

寫(xiě)在最后
至此,一個(gè)表達(dá)式解析就完成了,讓我們回過(guò)來(lái)再看這張圖:

我們可以發(fā)現(xiàn),其實(shí)Seq3 的作用其實(shí)僅僅是標(biāo)識(shí)了一個(gè)組的開(kāi)始并記錄該組與同級(jí)別的其他元素的關(guān)聯(lián)關(guān)系,其實(shí),這里還可以進(jìn)行一次優(yōu)化:我們發(fā)現(xiàn)每當(dāng)一個(gè)組的開(kāi)始的第一個(gè)節(jié)點(diǎn)其前置關(guān)聯(lián)關(guān)系一定是Begin,Deep+1,實(shí)際上我們可以考慮將Group的關(guān)聯(lián)關(guān)系放在這個(gè)節(jié)點(diǎn)上,然后僅僅通過(guò)Deep的增減控制組的關(guān)系,這樣,我們就不需要類型為表達(dá)式或組的這個(gè)字段了,而且數(shù)組長(zhǎng)度也會(huì)因此減少,但是個(gè)人認(rèn)為理解起來(lái)會(huì)麻煩一點(diǎn)。這里說(shuō)一下大概改造思路,代碼就不放出來(lái)了:
- 將代碼中有關(guān)Type="GROUP"的判斷改為通過(guò)deep的差值=1進(jìn)行判斷
- 深度判斷入棧邏輯修改
- 在存儲(chǔ)關(guān)系符號(hào)的時(shí)候還要存儲(chǔ)一下這個(gè)關(guān)系符號(hào)對(duì)應(yīng)的深度
- 在處理同深度遺留元素時(shí),即:
computeBeforeEndGroup()方法中, 原方法是通過(guò)Begin元素進(jìn)行區(qū)分Group是否處理完成,現(xiàn)需要改成通過(guò)下一個(gè)符號(hào)的深度是否和當(dāng)前深度是否相同進(jìn)行判斷,并刪除掉有關(guān)BEGIN元素的彈出的邏輯
以上就是基于Java實(shí)現(xiàn)一個(gè)復(fù)雜關(guān)系表達(dá)式過(guò)濾器的詳細(xì)內(nèi)容,更多關(guān)于Java表達(dá)式過(guò)濾器的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
舉例分析Python中設(shè)計(jì)模式之外觀模式的運(yùn)用
這篇文章主要介紹了Python中設(shè)計(jì)模式之外觀模式的運(yùn)用,外觀模式主張以分多模塊進(jìn)行代碼管理而減少耦合,需要的朋友可以參考下2016-03-03
Spring不能注入Static變量的原因及Spring注入靜態(tài)變量
這篇文章主要介紹了Spring不能注入Static變量的原因及Spring注入靜態(tài)變量,需要的朋友可以參考下2016-01-01
Java服務(wù)端如何解決跨域問(wèn)題?CORS請(qǐng)求頭方式
這篇文章主要介紹了Java服務(wù)端如何解決跨域問(wèn)題?CORS請(qǐng)求頭方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-11-11
java中關(guān)于移位運(yùn)算符的demo與總結(jié)(推薦)
下面小編就為大家?guī)?lái)一篇java中關(guān)于移位運(yùn)算符的demo與總結(jié)(推薦)。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2016-05-05
Java設(shè)計(jì)模式之建造者模式(Builder模式)介紹
這篇文章主要介紹了Java設(shè)計(jì)模式之建造者模式(Builder模式)介紹,本文講解了為何使用建造者模式、如何使用建造者模式、Builder模式的應(yīng)用等內(nèi)容,需要的朋友可以參考下2015-03-03
Java中關(guān)于char類型變量能夠輸出中文的問(wèn)題
這篇文章主要介紹了Java中關(guān)于char類型變量能夠輸出中文的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-12-12
Java多線程編程中synchronized關(guān)鍵字的基礎(chǔ)用法講解
Java的synchronized關(guān)鍵字用于修飾線程同步,用以線程資源共享的目的等,下面就帶來(lái)簡(jiǎn)單的Java多線程編程中synchronized關(guān)鍵字的基礎(chǔ)用法講解2016-06-06

