Strategy模式定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换。本模式使得算法可独立于使用它的客户而变化。
类图:
案例: 不同的员工薪资计算不同。 没有实现Strategy模式的例子:
public class Employee { public static final int ENGINEER = 0; public static final int SALESMAN = 1; public static final int MANAGER = 2; private int type = 0; private int monthlySalary; private int commission; private int bonus; public Employee(int type, int monthlySalary) { this.type = type; this.monthlySalary = monthlySalary; } public int getType() { return type; } public int getMonthlySalary() { return monthlySalary; } public int getCommission() { return commission; } public void setCommission(int commission) { this.commission = commission; } public int getBonus() { return bonus; } public void setBonus(int bonus) { this.bonus = bonus; } public int payAmount() { int result = 0; switch(type) { case ENGINEER: result = monthlySalary; break; case SALESMAN: result = monthlySalary + commission; break; case MANAGER: result = monthlySalary + bonus; break; } return result; } }测试代码
public class EmployeeTest { @Test public void testEngineer() { Employee engineer = new Employee(Employee.ENGINEER, 8000); assertEquals(8000, engineer.payAmount()); engineer.setCommission(1000); engineer.setBonus(2000); assertEquals(8000, engineer.payAmount()); } @Test public void testSalesman() { Employee salesman = new Employee(Employee.SALESMAN, 8000); assertEquals(8000, salesman.payAmount()); salesman.setCommission(1000); salesman.setBonus(2000); assertEquals(9000, salesman.payAmount()); } @Test public void testManager() { Employee manager = new Employee(Employee.MANAGER, 8000); assertEquals(8000, manager.payAmount()); manager.setCommission(1000); manager.setBonus(2000); assertEquals(10000, manager.payAmount()); } }可以看到payAmount()方法有条件语句,不同类型的员工,薪资计算的算法不同,可以使用strategy模式重构: 每个类型码对应一个具体的strategy类, 每个strategy类定义一个payAmount(Employee employee)方法,Employee持有一个strategy类型的引用,它的payAmount委托给这个strategy引用对象,为了多态实现strategy, 提炼strategy接口。
public interface EmployeeType {//strategy interface public int payAmount(Employee employee); } public class Engineer implements EmployeeType { public int payAmount(Employee employee) { return employee.getMonthlySalary(); } } public class Salesman implements EmployeeType { public int payAmount(Employee employee) { return employee.getMonthlySalary() + employee.getCommission(); } } public class Manager implements EmployeeType { public int payAmount(Employee employee) { return employee.getMonthlySalary() + employee.getBonus(); } }修改Employee类,将类型码替换成strategy类型引用,payAmount()委托给strategy类型的引用对象的payAmount(Employee employee)方法:
public class Employee { private EmployeeType employeeType; private int monthlySalary; private int commission; private int bonus; public Employee(EmployeeType employeeType, int monthlySalary) { this.employeeType = employeeType; this.monthlySalary = monthlySalary; } public EmployeeType getType() { return employeeType; } public int getMonthlySalary() { return monthlySalary; } public int getCommission() { return commission; } public void setCommission(int commission) { this.commission = commission; } public int getBonus() { return bonus; } public void setBonus(int bonus) { this.bonus = bonus; } public int payAmount() { return employeeType.payAmount(this); } }总结:
Strategy 模式是通过对象组合方式去除复杂的条件逻辑,为什么不选择通过Context的类继承方式? a) 运行时动态替换算法。 b) 独立于客户端,无需修改客户端代码。
Strategy类如何访问Context类的数据? a) 将Context引用传递给Strategy类的计算方法(回调机制)。 b) 将所需的数据传递给Strategy类的计算方法。 c) 将所需的数据封装成类,通过参数对象传递给Strategy类的计算方法。 d) 将特殊数据传递给Strategy类的构造函数或初始化方法。
Context类中用于计算的辅助方法 是 放在Context类中还是Strategy类中? 如果只是与计算相关,将这些辅助方法移动到Strategy类中。