又是很長(zhǎng)時(shí)間沒有寫博客了(一個(gè)月)...最近在做一個(gè)SpringBoot+Vue的項(xiàng)目御板,所以一直在看spring相關(guān)的東西。今天要學(xué)習(xí)的跟spring
沒有關(guān)系蚀腿,是我在之前維護(hù)的一個(gè)測(cè)試工具是遇到的一個(gè)知識(shí)點(diǎn)--狀態(tài)機(jī)
這個(gè)測(cè)試的一個(gè)功能就是解析自己定義的一套腳本語(yǔ)法規(guī)則,涉及到對(duì)輸入的語(yǔ)句進(jìn)行解析,然后下發(fā)到對(duì)應(yīng)的執(zhí)行器去執(zhí)行亦镶。
之前的解析邏輯是用一個(gè)while循環(huán),對(duì)每一個(gè)字符判斷袱瓮,然后各種if...else和臨時(shí)變量...總之讀起來十分費(fèi)勁缤骨,并且總?cè)菀壮鯞UG,而且十分不容易修改尺借,因?yàn)槊恳粋€(gè)修改都很容易影響到原來的解析結(jié)果绊起。
于是我把這段解析的代碼重構(gòu)了一遍,就是使用了狀態(tài)機(jī)的思想燎斩。
什么是狀態(tài)機(jī)
有限狀態(tài)機(jī)(英語(yǔ):finite-state machine虱歪,縮寫:FSM)又稱有限狀態(tài)自動(dòng)機(jī),簡(jiǎn)稱狀態(tài)機(jī)栅表,是表示有限個(gè)狀態(tài)以及在這些狀態(tài)之間的轉(zhuǎn)移和動(dòng)作等行為的數(shù)學(xué)模型笋鄙。
狀態(tài)機(jī)可歸納為4個(gè)要素,即現(xiàn)態(tài)怪瓶、條件萧落、動(dòng)作、次態(tài)洗贰≌裔“現(xiàn)態(tài)”和“條件”是因,“動(dòng)作”和“次態(tài)”是果:
現(xiàn)態(tài):是指當(dāng)前所處的狀態(tài)哆姻。
條件:又稱為“事件”宣增。當(dāng)一個(gè)條件被滿足,將會(huì)觸發(fā)一個(gè)動(dòng)作矛缨,或者執(zhí)行一次狀態(tài)的遷移爹脾。
動(dòng)作:條件滿足后執(zhí)行的動(dòng)作帖旨。動(dòng)作執(zhí)行完畢后,可以遷移到新的狀態(tài)灵妨,也可以仍舊保持原狀態(tài)解阅。動(dòng)作不是必需的,當(dāng)條件滿足后泌霍,也可以不執(zhí)行任何動(dòng)作货抄,直接遷移到新狀態(tài)。
次態(tài):條件滿足后要遷往的新狀態(tài)朱转⌒返兀“次態(tài)”是相對(duì)于“現(xiàn)態(tài)”而言的,“次態(tài)”一旦被激活藤为,就轉(zhuǎn)變成新的“現(xiàn)態(tài)”了怪与。
不是太好理解,我也是copy網(wǎng)上的概念缅疟,我們下面會(huì)舉例子說明分别。
什么是狀態(tài)模式
- Context(環(huán)境類)
環(huán)境類又稱為上下文類,它是擁有多種狀態(tài)的對(duì)象存淫。由于環(huán)境類的狀態(tài)存在多樣性且在不同狀態(tài)下對(duì)象的行為有所不同耘斩,因此將狀態(tài)獨(dú)立出去形成單獨(dú)的狀態(tài)類。在環(huán)境類中維護(hù)一個(gè)抽象狀態(tài)類State的實(shí)例桅咆,這個(gè)實(shí)例定義當(dāng)前狀態(tài)括授,在具體實(shí)現(xiàn)時(shí),它是一個(gè)State子類的對(duì)象轧邪。
- State(抽象狀態(tài)類)
它用于定義一個(gè)接口以封裝與環(huán)境類的一個(gè)特定狀態(tài)相關(guān)的行為刽脖,在抽象狀態(tài)類中聲明了各種不同狀態(tài)對(duì)應(yīng)的方法,而在其子類中實(shí)現(xiàn)類這些方法忌愚,由于不同狀態(tài)下對(duì)象的行為可能不同,因此在不同子類中方法的實(shí)現(xiàn)可能存在不同却邓,相同的方法可以寫在抽象狀態(tài)類中硕糊。
- ConcreteState(具體狀態(tài)類)
它是抽象狀態(tài)類的子類,每一個(gè)子類實(shí)現(xiàn)一個(gè)與環(huán)境類的一個(gè)狀態(tài)相關(guān)的行為腊徙,每一個(gè)具體狀態(tài)類對(duì)應(yīng)環(huán)境的一個(gè)具體狀態(tài)简十,不同的具體狀態(tài)類其行為有所不同。
同樣的撬腾,下面會(huì)舉例說明螟蝙。
一個(gè)例子
假設(shè)現(xiàn)在有這么一個(gè)需求:給出一段java程序,要求刪除其中的注釋并返回刪除注釋之后的代碼民傻。
想想怎么去實(shí)現(xiàn)這個(gè)功能胰默?初步的思路是在一個(gè)while循環(huán)里面场斑,遍歷這個(gè)String,對(duì)每個(gè)字符進(jìn)行判斷牵署,然后是if else等等...功能肯定是可以實(shí)現(xiàn)的漏隐,但是我們有一個(gè)更加合適的套路,就是使用狀態(tài)機(jī)奴迅。
設(shè)計(jì)狀態(tài)機(jī)如下:
- 設(shè)正常狀態(tài)為0青责,并且初始為正常狀態(tài)
每遍歷一個(gè)字符,就依次檢查下列條件取具,若成立或全部檢查完畢脖隶,則回到這里檢查下一個(gè)字符
狀態(tài)0中遇到/,說明可能會(huì)遇到注釋暇检,則進(jìn)入狀態(tài)1 例子: int a = b; /
狀態(tài)1中遇到/浩村,說明進(jìn)入單行注釋部分,則進(jìn)入狀態(tài)2 例子: int a = b; //
狀態(tài)1中遇到占哟,說明進(jìn)入多行注釋部分心墅,則進(jìn)入狀態(tài)3 例子: int a= b; /
狀態(tài)1中沒有遇到*或/,說明/是路徑符號(hào)或除號(hào)榨乎,則恢復(fù)狀態(tài)0 例子: 8/3
狀態(tài)2中遇到回車符\n怎燥,說明單行注釋結(jié)束,則恢復(fù)狀態(tài)0 例子: int a = b; //hehe
狀態(tài)2中不是遇到回車符\n蜜暑,說明單行注釋還在繼續(xù)铐姚,則維持狀態(tài)2 例子: int a = b; //hehe
狀態(tài)3中遇到,說明多行注釋可能要結(jié)束肛捍,則進(jìn)入狀態(tài)4 例子: int a = b; /heh*
狀態(tài)3中不是遇到隐绵,說明多行注釋還在繼續(xù),則維持狀態(tài)3 例子: int a = b; /hehe
狀態(tài)4中遇到/拙毫,說明多行注釋要結(jié)束依许,則恢復(fù)狀態(tài)0 例子: int a = b; /hehe/
狀態(tài)4中不是遇到/,說明多行注釋只是遇到缀蹄,還要繼續(xù)峭跳,則恢復(fù)狀態(tài)3 例子: int a = b; /hehe*h
狀態(tài)圖:
if else實(shí)現(xiàn)狀態(tài)機(jī)
package space.kyu.mode.state;
public class CodeProcessor1 {
private StringBuilder codeWithoutComment;
private String originCode;
public CodeProcessor1(String code) {
originCode = code;
}
public String clearComment() {
codeWithoutComment = new StringBuilder();
char c, state;
state = 0;
for (int i = 0; i < originCode.length(); ++i) {
c = getChar(i);
if (state == 0) {
if (c == '/') {
state = 1;
} else {
putChar(c); // action
}
} else if (state == 1) {
if (c == '/') // 例子: int a = b; //
{
state = 2;
} else if (c == '*') // 例子: int a= b; /*
{
state = 3;
} else // 例子: <common/md5.h> or 8/3
{
state = 0;
putChar('/'); // action
putChar(c); // action
}
} else if (state == 2) {
if (c == '\n') // 例子: int a = b; //hehe
{
state = 0;
putChar(c); // action
}
// 例子: int a = b; //hehe
} else if (state == 3) {
if (c == '*') // 例子: int a = b; /*heh*
{
state = 4;
}
// 例子: int a = b; /*hehe
} else if (state == 4) {
if (c == '/') // 例子: int a = b; /*hehe*/
{
state = 0;
} else // 例子: int a = b; /*hehe*h
{
state = 3;
}
} else {
System.out.println("state error!");
}
}
return codeWithoutComment.toString();
}
private char getChar(int i) {
return originCode.charAt(i);
}
private void putChar(char c) {
codeWithoutComment.append(c);
}
public static void main(String[] args) {
String code = " public static void main(String[] args) {" + "\n"
+ " /*hehe " + "\n"
+ " hehe " + "\n"
+ " */ " + "\n"
+ " /*hehe*/" + "\n"
+ " int a, int b; " + "\n"
+ " /* hehe */ " + "\n"
+ " //hehe" + "\n"
+ " a = 4+2; //hehe" + "\n"
+ " b = a;" + "\n"
+ " String file = \"/tmp/log.log\"" + "\n"
+ " }";
System.out.println(code);
System.out.println("*******************************");
CodeProcessor1 process = new CodeProcessor1(code);
String str = process.clearComment();
System.out.println(str);
}
}
輸出結(jié)果:
public static void main(String[] args) {
/*hehe
hehe
*/
/*hehe*/
int a, int b;
/* hehe */
//hehe
a = 4+2; //hehe
b = a;
String file = "/tmp/log.log"
}
*******************************
public static void main(String[] args) {
int a, int b;
a = 4+2;
b = a;
String file = "/tmp/log.log"
}
狀態(tài)模式實(shí)現(xiàn)狀態(tài)機(jī)
package space.kyu.mode.state.interfac;
public class CodeProcessor2 {
InputState currentState;
StringBuilder codeWithoutComment;
String originCode;
public CodeProcessor2(String code) {
originCode = code;
currentState = new Normal();
}
public String clearComment() {
codeWithoutComment = new StringBuilder();
for (int i = 0; i < originCode.length(); ++i) {
char charAt = getChar(i);
currentState.handleInput(charAt, this);
}
return codeWithoutComment.toString();
}
private char getChar(int i) {
return originCode.charAt(i);
}
public void putChar(char c) {
codeWithoutComment.append(c);
}
public static void main(String[] args) {
String code = " public static void main(String[] args) {" + "\n"
+ " /*hehe " + "\n"
+ " hehe " + "\n"
+ " */ " + "\n"
+ " /*hehe*/" + "\n"
+ " int a, int b; " + "\n"
+ " /* hehe */ " + "\n"
+ " //hehe" + "\n"
+ " a = 4+2; //hehe" + "\n"
+ " b = a;" + "\n"
+ " String file = \"/tmp/log.log\"" + "\n"
+ " }";
System.out.println(code);
System.out.println("*******************************");
CodeProcessor2 process = new CodeProcessor2(code);
String str = process.clearComment();
System.out.println(str);
}
}
abstract class InputState {
protected char backslash = '/';
protected char asterisk = '*';
protected char lineBreaks = '\n';
abstract void handleInput(char charAt, CodeProcessor2 processor);
}
class Normal extends InputState {
@Override
public void handleInput(char charAt, CodeProcessor2 processor) {
if (charAt == backslash) {
processor.currentState = new CommentSymbol();
} else {
processor.putChar(charAt);
}
}
}
class CommentSymbol extends InputState {
@Override
public void handleInput(char charAt, CodeProcessor2 processor) {
if (charAt == backslash) {
processor.currentState = new SinglelineComment();
} else if (charAt == asterisk) {
processor.currentState = new MutilineComment();
} else {
processor.putChar('/');
processor.putChar(charAt);
processor.currentState = new Normal();
}
}
}
class SinglelineComment extends InputState {
@Override
public void handleInput(char charAt, CodeProcessor2 processor) {
if (charAt == lineBreaks) {
processor.putChar(charAt);
processor.currentState = new Normal();
}
}
}
class MutilineComment extends InputState {
@Override
public void handleInput(char charAt, CodeProcessor2 processor) {
if (charAt == asterisk) {
processor.currentState = new MutilineCommentEnding();
}
}
}
class MutilineCommentEnding extends InputState {
@Override
public void handleInput(char charAt, CodeProcessor2 processor) {
if (charAt == backslash) {
processor.currentState = new Normal();
} else {
processor.currentState = new MutilineComment();
}
}
}
其中:
CodeProcessor2 為 Context(環(huán)境類)
InputState 為 State(抽象狀態(tài)類)
Normal等繼承了InputState的類 為 ConcreteState(具體狀態(tài)類)
輸出結(jié)果:
public static void main(String[] args) {
/*hehe
hehe
*/
/*hehe*/
int a, int b;
/* hehe */
//hehe
a = 4+2; //hehe
b = a;
String file = "/tmp/log.log"
}
*******************************
public static void main(String[] args) {
int a, int b;
a = 4+2;
b = a;
String file = "/tmp/log.log"
}
enum實(shí)現(xiàn)狀態(tài)機(jī)
利用java中提供的enum實(shí)現(xiàn)狀態(tài)機(jī)也是狀態(tài)模式的一種,這樣讓代碼更整潔并且不會(huì)產(chǎn)生很多的類導(dǎo)致類膨脹缺前。
package space.kyu.mode.state;
public class CodeProcessor {
InputState currentState;
StringBuilder codeWithoutComment;
String originCode;
public CodeProcessor(String code) {
originCode = code;
currentState = States.NORMAL;
}
public String clearComment() {
codeWithoutComment = new StringBuilder();
for(int i = 0; i < originCode.length(); ++i){
char charAt = getChar(i);
currentState.handleInput(charAt, this);
}
return codeWithoutComment.toString();
}
private char getChar(int i) {
return originCode.charAt(i);
}
public void putChar(char c){
codeWithoutComment.append(c);
}
public static void main(String[] args) {
String code = " public static void main(String[] args) {" + "\n"
+ " /*hehe " + "\n"
+ " hehe " + "\n"
+ " */ " + "\n"
+ " /*hehe*/" + "\n"
+ " int a, int b; " + "\n"
+ " /* hehe */ " + "\n"
+ " //hehe" + "\n"
+ " a = 4+2; //hehe" + "\n"
+ " b = a;" + "\n"
+ " String file = \"/tmp/log.log\"" + "\n"
+ " }";
System.out.println(code);
System.out.println("*******************************");
CodeProcessor process = new CodeProcessor(code);
String str = process.clearComment();
System.out.println(str);
}
}
interface InputState {
void handleInput(char charAt, CodeProcessor processor);
}
enum States implements InputState {
/**
* 正常狀態(tài)
*/
NORMAL{
@Override
public void handleInput(char charAt, CodeProcessor processor) {
if (charAt == backslash) {
processor.currentState = COMMENT_SYMBOL;
} else {
processor.putChar(charAt);
}
}
},
/**
* 遇到注釋符 /
*/
COMMENT_SYMBOL{
@Override
public void handleInput(char charAt, CodeProcessor processor) {
if (charAt == backslash) {
processor.currentState = SINGLE_LINE_COMMENT;
} else if (charAt == asterisk) {
processor.currentState = MUTI_LINE_COMMENT;
} else {
processor.putChar('/');
processor.putChar(charAt);
processor.currentState = NORMAL;
}
}
},
/**
* 進(jìn)入單行注釋
*/
SINGLE_LINE_COMMENT{
@Override
public void handleInput(char charAt, CodeProcessor processor) {
if (charAt == lineBreaks) {
processor.putChar(charAt);
processor.currentState = NORMAL;
}
}
},
/**
* 進(jìn)入多行注釋
*/
MUTI_LINE_COMMENT{
@Override
public void handleInput(char charAt, CodeProcessor processor) {
if (charAt == asterisk) {
processor.currentState = MUTI_LINE_COMMENT_ENDDING;
}
}
},
/**
* 多行注釋 遇到 *
*/
MUTI_LINE_COMMENT_ENDDING{
@Override
public void handleInput(char charAt, CodeProcessor processor) {
if (charAt == backslash) {
processor.currentState = NORMAL;
} else {
processor.currentState = MUTI_LINE_COMMENT;
}
}
};
char backslash = '/';
char asterisk = '*';
char lineBreaks = '\n';
}
輸出結(jié)果:
public static void main(String[] args) {
/*hehe
hehe
*/
/*hehe*/
int a, int b;
/* hehe */
//hehe
a = 4+2; //hehe
b = a;
String file = "/tmp/log.log"
}
*******************************
public static void main(String[] args) {
int a, int b;
a = 4+2;
b = a;
String file = "/tmp/log.log"
}