假设我们现在需要计算一个员工的个人所得税,于是我们写了如下工具类,传入基本工资和奖金即可调用calculate得出应纳税额。
税率计算类,模拟的是Thread类。
/**
* 税率计算类。模拟Thread类的“模板方法模式”
*/
public class TaxCalculator {
//工资
private final double salary;
//奖金
private final double bonus;
//构造器
public TaxCalculator(double salary, double bonus) {
this.salary = salary;
this.bonus = bonus;
}
/**
* 计算税率的业务逻辑。
* 供子类重写。相当于Thread的run方法
*/
protected double calcTax() {
return 0.0d;
}
/**
* 执行税率计算。
* 供外部调用。相当于Thread的start方法
* @return
*/
public double calculate() {
return this.calcTax();
}
public double getSalary() {
return salary;
}
public double getBonus() {
return bonus;
}
}
测试一下:
public class TaxCalculatorMain {
public static void main(String[] args) {
TaxCalculator taxCalaculator = new TaxCalculator(10000d, 2000d) {
@Override
//重写计算税率方法,实现自己的业务逻辑
protected double calcTax() {
return getSalary() * 0.1 + getBonus() * 0.15;
}
};
double calcute = taxCalaculator.calculate();
System.out.println(calcute);
}
}
这样写有什么问题?我们将应纳税额的计算写死了:salary * 0.1 + bonus * 0.15,而税率并非一成不变的,客户提出需求变动也是常有的事!难道每次需求变更我们都要手动更改这部分代码吗?
这时策略模式来帮忙:当我们的需求的输入是不变的,但输出需要根据不同的策略做出相应的调整时,我们可以将这部分的逻辑抽取成一个接口:
public interface CalculatorStrategy {
double calculate(double salary,double bonus);
}
具体策略实现:
public class SimpleCalculatorStrategy implements CalculatorStrategy {
private final static double SALARY_RATE = 0.1;
private final static double BOUNS_RATE = 0.15;
@Override
public double calculate(double salary, double bonus) {
return salary * SALARY_RATE + bonus * BOUNS_RATE;
}
}
上面的税率计算类调整一下,业务代码仅调用接口:
/**
* 税率计算器。模拟Thread类的“模板方法模式”
*/
public class TaxCalculator {
//工资
private final double salary;
//奖金
private final double bonus;
//定义抽象出来计算税率的接口
private CalculatorStrategy calculatorStrategy;
//构造器
public TaxCalculator(double salary, double bonus) {
this.salary = salary;
this.bonus = bonus;
}
/**
* 相当于Thread的run方法
* 计算税率的业务逻辑放到策略类里面,不需要再重写了
*
*/
protected double calcTax() {
return calculatorStrategy.calculate(salary, bonus);
}
/**
* 相当于Thread的start方法
* 执行税率计算。供外部调用。
* @return
*/
public double calculate() {
return this.calcTax();
}
public void setCalculatorStrategy(CalculatorStrategy calculatorStrategy) {
this.calculatorStrategy = calculatorStrategy;
}
}
测试一下:
public class TaxCalculatorMain {
public static void main(String[] args) {
// 只负责执行税率计算任务。类似于Thread
TaxCalaculator taxCalaculator = new TaxCalaculator(10000d, 2000d);
// 负责具体税率任务逻辑。类似于Runnable
CalculatorStrategy calculatorStrategy = new SimpleCalculatorStrategy();
taxCalaculator.setCalculatorStrategy(calculatorStrategy);
// taxCalaculator.setCalculatorStrategy(((salary, bonus) -> salary * 0.1 + bonus * 0.15));
System.out.println(taxCalaculator.calculate());
}
}
假如,税率计算规则变成了(salary + bonus) * 1.5,我们只需新增一个策略类:
public class AnotherTaxCalculatorStrategy implements CalculatorStrategy {
@Override
public double calculate(double salary, double bonus) {
return (salary + bonus) * 1.5;
}
}
这与Thread中的逻辑执行单元run抽取成一个接口Runnable有着异曲同工之妙。因为实际业务中,需要提交给线程执行的任务我们是无法预料的,抽取成一个接口之后就给我们的应用程序带来了很大的灵活性。
Thread的职责就是执行任务,而将业务逻辑单独抽取出来作为一个逻辑执行单元,当需要执行时提交给线程即可。于是就有了Runnable接口。
参考:
《汪文君 JAVA 多线程编程实战》
https://www.jianshu.com/p/1f491f814c27