用策略模式替换条件逻辑
最后修改时间:2023 年 8 月 24 日当您的方法包含大量条件逻辑(即 if 语句)时,您就是在自找麻烦。众所周知,条件逻辑很难管理,并且可能会导致您在单个方法内创建整个状态机。
分析示例应用程序
这是一个简短的例子。比方说,有一种方法可以根据一个人的收入来计算保险费用:
package ifs;
public class IfElseDemo {
    public double calculateInsurance(double income) {
        if (income <= 10000) {
            return income*0.365;
        } else if (income <= 30000) {
            return (income-10000)*0.2+35600;
        } else if (income <= 60000) {
            return (income-30000)*0.1+76500;
        } else {
            return (income-60000)*0.02+105600;
        }
    }
}我们来分析一下这个例子。在这里我们看到四个“收入带宽度”,分为四种计算策略。一般来说,它们符合以下公式:
(income - adjustment) * weight + constant我们的目标是提供单独的类来计算每个策略,并对原始类进行改造,使其更加透明。
创建并运行测试
首先,让我们确保该类能够正常工作。为此,请使用JUnit4测试框架创建一个测试类。
将插入符号放在类名称处,然后按(或单击)。从建议的意图操作列表中,选择创建测试:AltEnter

在“创建测试”对话框中,从“测试库”列表中选择“JUnit4”,单击“修复”(如果没有 JUnit 库),然后单击“确定”:

存根测试类已创建。但是,您必须提供一些有意义的代码,使用提供的快速修复来创建导入语句:
import org.junit.Test;
import static org.junit.Assert.*;
public class IfElseDemoTest {
    @Test
    public void low() {
    assertEquals(1825, insuranceFor(5000), 0.01);
    }
    @Test
    public void medium() {
    assertEquals(38600, insuranceFor(25000), 0.01);
    }
    @Test
    public void high() {
    assertEquals(78500, insuranceFor(50000), 0.01);
    }
    @Test
    public void veryHigh() {
    assertEquals(106400, insuranceFor(100_000), 0.01);
    }
    private double insuranceFor(double income) {
    return new IfElseDemo().calculateInsurance(income);
    }
}现在让我们通过单击左侧装订线中的运行按钮或按以下键来运行此测试:CtrlShiftF10

所有 4 项测试均通过:

提取方法
接下来,提出原始类,将插入符号放在表达式上
(income-60000)*0.02+105600并调用“提取方法对话框”对话框:CtrlAlt0M

您将得到以下代码:
package ifs;
class IfElseDemo {
   public double calculateInsurance(double income) {
       if (income <= 10000) {
           return income*0.365;
       } else if (income <= 30000) {
           return (income-10000)*0.2+35600;
       } else if (income <= 60000) {
           return (income-30000)*0.1+76500;
       } else {
           return calculateInsuranceVeryHigh(income);
   }
}
       public double calculateInsuranceVeryHigh(double income) {
           return (income-60000)*0.02+105600;
       }
}接下来,对 60000、0.02 和 105600 片段使用相同的 Extract Method 重构,并创建方法getAdjustment、getWeight和getConstant:
package ifs;
class IfElseDemo {
    public double calculateInsurance(double income) {
        if (income <= 10000) {
            return income*0.365;
        } else if (income <= 30000) {
            return (income-10000)*0.2+35600;
        } else if (income <= 60000) {
            return (income-30000)*0.1+76500;
        } else {
            return calculateInsuranceVeryHigh(income);
    }
    public double calculateInsuranceVeryHigh(double income) {
        return (income- getAdjustment())* getWeight() + getConstant();
    }
    public int getConstant() {
        return 105600;
    }
    public double getWeight() {
        return 0.02;
    }
    public int getAdjustment() {
        return 60000;
    }
}使用 Extract Delegate 重构
接下来,选择上一章中创建的所有方法,并调用Extract Delegate重构:

新创建的类的名称为InsuranceStrategyVeryHigh。
然后从类中删除所有选定的方法IfElseDemo。因此,您将获得以下两个类:
package ifs;
class IfElseDemo {
    private final InsuranceStrategyVeryHigh insuranceStrategyVeryHigh = new InsuranceStrategyVeryHigh();
    public double calculateInsurance(double income) {
        if (income <= 10000) {
            return income*0.365;
        } else if (income <= 30000) {
            return (income-10000)*0.2+35600;
        } else if (income <= 60000) {
            return (income-30000)*0.1+76500;
        } else {
            return insuranceStrategyVeryHigh.calculateInsuranceVeryHigh(income);
    }
}和
package ifs;
public class InsuranceStrategyVeryHigh {
   public InsuranceStrategyVeryHigh() {
   }
   public double calculateInsuranceVeryHigh(double income) {
       return (income - getAdjustment()) * getWeight() + getConstant();
   }
   public int getConstant() {
       return 105600;
   }
   public double getWeight() {
       return 0.02;
   }
   public int getAdjustment() {
       return 60000;
   }
}微调
这段代码需要一些修改。首先,让我们改变类IfElseDemo:
- 将字段重命名为ShiftF6 - insuranceStrategyVeryHigh- strategy
- 使该字段不是最终的。 
- 使用意图操作,将其分为声明和初始化:  
- 将初始化向下移动到相应的 - if-else分支。
获取以下代码:
package ifs;
class IfElseDemo {
    private InsuranceStrategyVeryHigh strategy;
    public double calculateInsurance(double income) {
        if (income <= 10000) {
            return income*0.365;
        } else if (income <= 30000) {
            return (income-10000)*0.2+35600;
        } else if (income <= 60000) {
            return (income-30000)*0.1+76500;
        } else {
            strategy = new InsuranceStrategyVeryHigh();
            return strategy.calculateInsuranceVeryHigh(income);
        }
    }
}接下来,让我们修改该类InsuranceStrategyVeryHigh-  为其调用Extract 超类重构。

注意“提取超类”对话框中的设置:
- 要生成的超类的名称是 - InsuranceStrategy。
- 该类的所有方法 - InsuranceStrategyVeryHigh都会被检查——这意味着它们将被包含在超类中。
- 该方法 - calculateInsuranceStrategyVeryHigh仍然是非抽象的;通过选择“Make Abstract”复选框将所有其他方法设为抽象。
InsuranceStrategyVeryHigh同意将class(类中)的用法替换IfElseDemo为超类,得到如下InsuranceStrategy类:
package ifs;
public abstract class InsuranceStrategy {
    public double calculateInsuranceVeryHigh(double income) {
        return (income - getAdjustment()) * getWeight() + getConstant();
    }
    public abstract int getConstant();
    public abstract double getWeight();
    public abstract int getAdjustment();
}实现抽象类
接下来,对于这个抽象类,使用Implement Abstract Class意图来创建所有策略的实现:

将新的实现类命名为InsuranceStrategyLow,InsuranceStrategyMedium和InsuranceStrategyHigh。
对于所有新的实现,return为方法getAdjustment()和提供正确的语句。getWeight()getConstant()
因此,所有实现类应该与 类 类似InsuranceStrategyVeryHigh,但具有特定于策略的调整、权重和常量。例如:
package ifs;
public class InsuranceStrategyMedium extends InsuranceStrategy {
    @Override
    public int getConstant() {
        return 35600;
    }
    @Override
    public double getWeight() {
        return 0.2;
    }
    @Override
    public int getAdjustment() {
        return 10000;
    }
}请注意,在所有新创建的实现类中,类名都是灰色的——到目前为止它们还没有被使用。
接下来,提出 class IfElseDemo,并修改所有分支主体,以便它们初始化字段strategy,就像在最后一个分支中一样:
package ifs;
class IfElseDemo {
    private InsuranceStrategy strategy;
    public double calculateInsurance(double income) {
        if (income <= 10000) {
            strategy = new InsuranceStrategyLow();
            return strategy.calculateInsuranceVeryHigh(income);
        } else if (income <= 30000) {
            strategy = new InsuranceStrategyMedium();
            return strategy.calculateInsuranceVeryHigh(income);
        } else if (income <= 60000) {
            strategy = new InsuranceStrategyHigh();
            return strategy.calculateInsuranceVeryHigh(income);
        } else {
            strategy = new InsuranceStrategyVeryHigh();
            return strategy.calculateInsuranceVeryHigh(income);
        }
    }
}最后,重命名该方法calculateInsuranceVeryHigh:提出该类InsuranceStrategy,将脱字符号放在方法名称处,然后按。新名称应该是.ShiftF6calculate
好结局
最后享受代码:
package ifs;
class IfElseDemo {
    private InsuranceStrategy strategy;
    public double calculateInsurance(double income) {
        if (income <= 10000) {
            strategy = new InsuranceStrategyLow();
            return strategy.calculate(income);
        } else if (income <= 30000) {
            strategy = new InsuranceStrategyMedium();
            return strategy.calculate(income);
        } else if (income <= 60000) {
            strategy = new InsuranceStrategyHigh();
            return strategy.calculate(income);
        } else {
            strategy = new InsuranceStrategyVeryHigh();
            return strategy.calculate(income);
        }
    }
}接下来,让我们再次运行测试类。所有测试都应该通过——我们已经重构了代码,但它仍然产生相同的结果。
感谢您的反馈意见!