概述
ifelse是任何編程語(yǔ)言的重要組成部分衷咽。但是我們編寫(xiě)了大量嵌套的if語(yǔ)句,這使得我們的代碼更加復(fù)雜和難以維護(hù)走净。
接下來(lái)银择,讓我們探索如何簡(jiǎn)化代碼的中的ifelse語(yǔ)句寫(xiě)法。
案例研究
我們經(jīng)常遇到涉及很多條件的業(yè)務(wù)邏輯懦尝,并且每個(gè)邏輯都需要不同的處理方式知纷。以Calculator類為例。我們將有一個(gè)方法陵霉,它接受兩個(gè)數(shù)字和一個(gè)運(yùn)算符作為輸入琅轧,并根據(jù)操作返回結(jié)果:
public int calculate(int a, int b, String operator) {
int result = Integer.MIN_VALUE;
if ("add".equals(operator)) {
result = a + b;
} else if ("multiply".equals(operator)) {
result = a * b;
} else if ("divide".equals(operator)) {
result = a / b;
} else if ("subtract".equals(operator)) {
result = a - b;
}
return result;
}
我們也可以使用switch語(yǔ)句來(lái)實(shí)現(xiàn)它:
public int calculateUsingSwitch(int a, int b, String operator) {
switch (operator) {
case "add":
result = a + b;
break;
// other cases
}
return result;
}
在典型的開(kāi)發(fā)中,if語(yǔ)句可能會(huì)變得更大踊挠,更復(fù)雜乍桂。此外,當(dāng)存在復(fù)雜條件時(shí)效床,switch語(yǔ)句不適合睹酌。
擁有嵌套決策結(jié)構(gòu)的另一個(gè)副作用是它們變得難以管理。例如剩檀,如果我們需要添加一個(gè)新的運(yùn)算符憋沿,我們必須添加一個(gè)新的if語(yǔ)句并實(shí)現(xiàn)該操作。
重構(gòu)
可以通過(guò)設(shè)計(jì)模式沪猴,來(lái)達(dá)到我們要的效果辐啄。
工廠模式
很多時(shí)候,我們遇到ifelse結(jié)構(gòu)运嗜,最終在每個(gè)分支中執(zhí)行類似的操作壶辜。這提供了提取工廠方法的機(jī)會(huì),該工廠方法返回給定類型的對(duì)象并基于具體對(duì)象行為執(zhí)行操作担租。
對(duì)于我們的示例砸民,讓我們定義一個(gè)具有單個(gè)apply方法的Operation接口:
public interface Operation {
int apply(int a, int b);
}
該方法將兩個(gè)數(shù)字作為輸入并返回結(jié)果。讓我們定義一個(gè)用于執(zhí)行添加的類:
public class Addition implements Operation {
@Override
public int apply(int a, int b) {
return a + b;
}
}
我們現(xiàn)在將實(shí)現(xiàn)一個(gè)工廠類翩活,它根據(jù)給定的運(yùn)算符返回Operation的實(shí)例:
public class OperatorFactory {
static Map<String, Operation> operationMap = new HashMap<>();
static {
operationMap.put("add", new Addition());
operationMap.put("divide", new Division());
// more operators
}
public static Optional<Operation> getOperation(String operator) {
return Optional.ofNullable(operationMap.get(operator));
}
}
現(xiàn)在阱洪,在Calculator類中,我們可以查詢工廠以獲取相關(guān)操作并應(yīng)用源數(shù):
public int calculateUsingFactory(int a, int b, String operator) {
Operation targetOperation = OperatorFactory
.getOperation(operator)
.orElseThrow(() -> new IllegalArgumentException("Invalid Operator"));
return targetOperation.apply(a, b);
}
在這個(gè)例子中菠镇,我們已經(jīng)看到了如何將責(zé)任委托給工廠類提供的松散耦合對(duì)象冗荸。但是有可能嵌套的if語(yǔ)句只是轉(zhuǎn)移到了工廠類,這違背了我們的目的利耍。
或者蚌本,我們可以在Map中維護(hù)一個(gè)對(duì)象存儲(chǔ)庫(kù)盔粹,可以查詢?cè)摯鎯?chǔ)庫(kù)以進(jìn)行快速查找。正如我們所見(jiàn)程癌,OperatorFactory#operationMap服務(wù)于我們的目的舷嗡。我們還可以在運(yùn)行時(shí)初始化Map并將它們配置為查找。
使用枚舉
除了使用Map之外嵌莉,我們還可以使用Enum來(lái)標(biāo)記特定的業(yè)務(wù)邏輯进萄。之后,我們可以在嵌套的if語(yǔ)句或switch case 語(yǔ)句中使用它們锐峭≈惺螅或者,我們也可以將它們用作對(duì)象的工廠并制定策略以執(zhí)行相關(guān)的業(yè)務(wù)邏輯沿癞。
這樣可以減少嵌套if語(yǔ)句的數(shù)量援雇,并將責(zé)任委托給單個(gè)Enum值。
讓我們看看我們?nèi)绾螌?shí)現(xiàn)它椎扬。首先惫搏,我們需要定義我們的枚舉:
public enum Operator {
ADD, MULTIPLY, SUBTRACT, DIVIDE
}
可以觀察到,這些值是不同運(yùn)算符的標(biāo)簽蚕涤,將進(jìn)一步用于計(jì)算筐赔。我們總是可以選擇在嵌套的if語(yǔ)句或switch case中使用這些值作為不同的條件,但讓我們?cè)O(shè)計(jì)一種將邏輯委托給Enum本身的替代方法揖铜。
我們將為每個(gè)Enum值定義方法并進(jìn)行計(jì)算川陆。例如:
ADD {
@Override
public int apply(int a, int b) {
return a + b;
}
},
// other operators
public abstract int apply(int a, int b);
然后在Calculator類中,我們可以定義一個(gè)執(zhí)行操作的方法:
public int calculate(int a, int b, Operator operator) {
return operator.apply(a, b);
}
現(xiàn)在蛮位,我們可以通過(guò)使用Operator#valueOf()
方法將String值轉(zhuǎn)換為Operator來(lái)調(diào)用該方法:
@Test
public void test() {
Calculator calculator = new Calculator();
int result = calculator.calculate(3, 4, Operator.valueOf("ADD"));
assertEquals(7, result);
}
命令模式
在前面的討論中,我們已經(jīng)看到使用工廠類來(lái)返回給定運(yùn)算符的正確業(yè)務(wù)對(duì)象的實(shí)例鳞绕。稍后失仁,業(yè)務(wù)對(duì)象用于在計(jì)算器中執(zhí)行計(jì)算。
我們還可以設(shè)計(jì)一個(gè)Calculator#calculate方法來(lái)接受可以在輸入上執(zhí)行的命令们何。這將是替換嵌套if語(yǔ)句的另一種方法萄焦。
我們首先定義我們的Command接口:
public interface Command {
Integer execute();
}
接下來(lái),讓我們實(shí)現(xiàn)一個(gè)AddCommand:
public class AddCommand implements Command {
// Instance variables
public AddCommand(int a, int b) {
this.a = a;
this.b = b;
}
@Override
public Integer execute() {
return a + b;
}
}
最后冤竹,讓我們?cè)贑alculator中引入一個(gè)接受并執(zhí)行Command的新方法:
public int calculate(Command command) {
return command.execute();
}
接下來(lái)拂封,我們可以通過(guò)實(shí)例化AddCommand調(diào)用計(jì)算并將其發(fā)送到Calculator#calculate方法:
@Test
public void test() {
Calculator calculator = new Calculator();
int result = calculator.calculate(new AddCommand(3, 7));
assertEquals(10, result);
}
規(guī)則引擎
當(dāng)我們最終編寫(xiě)大量嵌套if語(yǔ)句時(shí),每個(gè)條件都描述了一個(gè)業(yè)務(wù)規(guī)則鹦蠕,必須對(duì)其進(jìn)行評(píng)估才能處理正確的邏輯冒签。規(guī)則引擎從主代碼中獲取了這種復(fù)雜性。一個(gè)RuleEngine
評(píng)估規(guī)則和返回基于輸入的結(jié)果钟病。
讓我們通過(guò)設(shè)計(jì)一個(gè)簡(jiǎn)單的RuleEngine來(lái)演示一個(gè)例子萧恕,該RuleEngine通過(guò)一組規(guī)則處理Expression并返回所選規(guī)則的結(jié)果刚梭。首先,我們將定義一個(gè)Rule接口:
public interface Rule {
boolean evaluate(Expression expression);
Result getResult();
}
其次票唆,讓我們實(shí)現(xiàn)一個(gè)RuleEngine:
public class RuleEngine {
private static List<Rule> rules = new ArrayList<>();
static {
rules.add(new AddRule());
}
public Result process(Expression expression) {
Rule rule = rules
.stream()
.filter(r -> r.evaluate(expression))
.findFirst()
.orElseThrow(() -> new IllegalArgumentException("Expression does not matches any Rule"));
return rule.getResult();
}
}
所述RuleEngine接受一個(gè)表達(dá)對(duì)象朴读,并返回結(jié)果。現(xiàn)在走趋,讓我們將Expression類設(shè)計(jì)為一組包含兩個(gè)Integer對(duì)象的Operator衅金,它將被應(yīng)用:
public class Expression {
private Integer x;
private Integer y;
private Operator operator;
}
最后讓我們定義一個(gè)自定義的AddRule類,該類僅在指定ADD操作時(shí)進(jìn)行求值:
public class AddRule implements Rule {
@Override
public boolean evaluate(Expression expression) {
boolean evalResult = false;
if (expression.getOperator() == Operator.ADD) {
this.result = expression.getX() + expression.getY();
evalResult = true;
}
return evalResult;
}
}
我們現(xiàn)在將使用Expression調(diào)用RuleEngine:
@Test
public void test() {
Expression expression = new Expression(5, 5, Operator.ADD);
RuleEngine engine = new RuleEngine();
Result result = engine.process(expression);
assertNotNull(result);
assertEquals(10, result.getValue());
}
結(jié)論
通過(guò)這些設(shè)計(jì)模式簿煌,可以作為我們的ifelse語(yǔ)句的替代方案氮唯,具體用哪一種可以根據(jù)你的實(shí)際業(yè)務(wù)場(chǎng)景來(lái)決定。