摘要:程式導向設計和麵向物件設計的主要區別是:是否在業務邏輯層使用冗長的if else判斷。
本文分享自華為雲社群《從面向if-else程式設計升級為面向狀態程式設計,減少程式碼複雜度》,作者:breakDraw。
程式導向設計和麵向物件設計的主要區別是:是否在業務邏輯層使用冗長的if else判斷。如果你還在大量使用if else,當然,介面表現層除外,即使你使用Java/C#這樣完全物件導向的語言,也只能說明你的思維停留在傳統的程式導向語言上。
需求
有一個非常經典的數字校驗場景, 需求如下:
複雜度高的硬寫程式碼
這時候如果直接硬寫,大概率寫出容易複雜度巨高的程式碼,還容易遺漏而出錯。
例子如下:
class Solution {
public boolean isNumber(String s) {
int sign = 1;
int pointSign = 1;
int eSign = 1;
int numSign = -1;
int i = 0;
int n = s.length();
while(i<n){
if(s.charAt(i)>='0'&&s.charAt(i)<='9'){
numSign = 1;
sign = -1;
}else if(s.charAt(i)=='+'||s.charAt(i)=='-'){
if(sign>0){
sign = -sign;
}else{
return false;
}
if(i>0&&s.charAt(i-1)=='.'){
return false;
}
}else if(s.charAt(i)=='.'){
//numSign = -1;
if(pointSign>0){
pointSign = -pointSign;
}else{
return false;
}
if(i>0&&(s.charAt(i-1)=='e'||s.charAt(i-1)=='E')){
return false;
}
}else if(s.charAt(i)=='e'||s.charAt(i)=='E'){
if(eSign<0||numSign<0){
return false;
}
eSign = -1;
sign = 1;
numSign = -1;
pointSign = -1;
}else{
return false;
}
i++;
}
return numSign>0;
}
}
這段程式碼的複雜度為 21, 放在科目一考試直接不及格了,而且非常容易出錯,改著改著把自己改暈了,或者改漏了。
§ 狀態機優化
圖片引用自Leetcode官方題解,連結見:
https://leetcode-cn.com/probl...
可以看到校驗的過程可以組成一個狀態, 當遇到特定字元時,進入特定的狀態去判斷,並且該狀態後面只能接入有限的狀態。因此我們可以定義N個狀態,每個狀態定義X個狀態變化條件和變化狀態。
在java中用多個map即可進行維護這種關係。
可以寫出如下的程式碼, 雖然程式碼量看起來更高了,但是可維護性和複雜度變強不少。
class Solution {
public enum CharType {
NUMBER,
OP,
POINT,
E;
public static CharType toCharType(Character c) {
if (Character.isDigit(c)) {
return NUMBER;
} else if (c == '+' || c == '-') {
return OP;
} else if (c == '.') {
return POINT;
} else if (c =='e' || c == 'E') {
return E;
} else {
return null;
}
}
}
public enum State {
INIT(false),
OP1(false),
// 在.前面的數字
BEFORE_POINT_NUMBER(true),
// 前面沒數字的點
NO_BEFORE_NUMBER_POINT(false),
// 前面有數字的點
BEFORE_NUMBER_POINT(true),
// 點後面的數字
AFTER_POINT_NUMBER(true),
// e/E
OPE(false),
// E後面的符號
OP2(false),
// e後面的數字
AFTER_E_NUMBER(true);
// 是否可在這個狀態結束
private boolean canEnd;
State(boolean canEnd) {
this.canEnd = canEnd;
}
public boolean isCanEnd() {
return canEnd;
}
}
public Map<State, Map<CharType, State>> transferMap = new HashMap<>() {{
Map<CharType, State> map = new HashMap<>() {{
put(CharType.OP, State.OP1);
put(CharType.NUMBER, State.BEFORE_POINT_NUMBER);
put(CharType.POINT, State.NO_BEFORE_NUMBER_POINT);
}};
put(State.INIT, map);
map = new HashMap<>() {{
put(CharType.POINT, State.NO_BEFORE_NUMBER_POINT);
put(CharType.NUMBER, State.BEFORE_POINT_NUMBER);
}};
put(State.OP1, map);
map = new HashMap<>() {{
put(CharType.POINT, State.BEFORE_NUMBER_POINT);
put(CharType.NUMBER, State.BEFORE_POINT_NUMBER);
put(CharType.E, State.OPE);
}};
put(State.BEFORE_POINT_NUMBER, map);
map = new HashMap<>() {{
put(CharType.NUMBER, State.AFTER_POINT_NUMBER);
}};
put(State.NO_BEFORE_NUMBER_POINT, map);
map = new HashMap<>() {{
put(CharType.NUMBER, State.AFTER_POINT_NUMBER);
put(CharType.E, State.OPE);
}};
put(State.BEFORE_NUMBER_POINT, map);
map = new HashMap<>() {{
put(CharType.E, State.OPE);
put(CharType.NUMBER, State.AFTER_POINT_NUMBER);
}};
put(State.AFTER_POINT_NUMBER, map);
map = new HashMap<>() {{
put(CharType.OP, State.OP2);
put(CharType.NUMBER, State.AFTER_E_NUMBER);
}};
put(State.OPE, map);
map = new HashMap<>() {{
put(CharType.NUMBER, State.AFTER_E_NUMBER);
}};
put(State.OP2, map);
map = new HashMap<>() {{
put(CharType.NUMBER, State.AFTER_E_NUMBER);
}};
put(State.AFTER_E_NUMBER, map);
}};
public boolean isNumber(String s) {
State state = State.INIT;
for (char c : s.toCharArray()) {
Map<CharType, State> transMap = transferMap.get(state);
CharType charType = CharType.toCharType(c);
if (charType == null) {
return false;
}
if (!transMap.containsKey(charType)) {
return false;
}
// 狀態變更
state = transMap.get(charType);
}
return state.canEnd;
}
}
可以看到複雜度也只有8,不會複雜度超標。