策略模式是工作中比较常用的设计模式之一,将一些除了过程不同其他都一样的函数封装成策略,然后调用方自己去选择想让数据执行什么过程策略,简单的说:一个类的行为或其算法可以在运行时更改。
在有多种算法相似的情况下,使用 if...else 所带来的复杂和难以维护。
一个系统有许多许多类,而区分它们的只是他们直接的行为。
将这些算法封装成一个一个的类,任意地替换。
注意事项:如果一个系统的策略多于四个,就需要考虑使用混合模式,解决策略类膨胀的问题。
抽象策略接口
public interface Strategy {
public int doOperation(int num1, int num2);
}
实现策略类
public class OperationAdd implements Strategy{
@Override
public int doOperation(int num1, int num2) {
return num1 + num2;
}
}
public class OperationSubtract implements Strategy{
@Override
public int doOperation(int num1, int num2) {
return num1 - num2;
}
}
public class OperationMultiply implements Strategy{
@Override
public int doOperation(int num1, int num2) {
return num1 * num2;
}
}
创建 Context 类
public class Context {
private Strategy strategy;
public void setStrategy(Strategy strategy) {
this.strategy = strategy;
}
public int executeStrategy(List source) {
return strategy.doOperation(source);
}
}
使用 Context 来查看当它改变策略 Strategy 时的行为变化。
public int execute(String type) {
// 创建上下文
Context context = new Context();
// 这里选择策略
switch (type) {
case "add":
context.setStrategy(new OperationAdd());
break;
case "subtract":
context.setStrategy(new OperationSubtract());
break;
case "multiply":
context.setStrategy(new OperationMultiply());
break;
default:
throw new IllegalArgumentException("rankType not found");
}
// 然后执行策略
return context.executeStrategy(4, 4);
}
一个接口、三个策略类,还是比较啰嗦的。每新增一个策略类还要修改实例(可以使用抽象工厂解决,但是复杂度又上升了)。
枚举策略类
public enum OperationEnum implements Strategy {
add {
@Override
public int doOperation(int num1, int num2) {
return num1 + num2;
}
},
subtract {
@Override
public int doOperation(int num1, int num2) {
return num1 - num2;
}
},
multiply {
@Override
public int doOperation(int num1, int num2) {
return num1 * num2;
}
};
// 这里定义了策略接口,可以选择使用抽象方法、也可以选择实现接口的方式
// public abstract int doOperation(int num1, int num2);
}
调用类
public int execute(String type) {
// 获取策略,这里如果未匹配会抛 IllegalArgumentException异常
OperationEnum operation = OperationEnum.valueOf(type);
// 然后执行策略
return operation.doOperation(4, 4);
}
如果策略简单的话,基于枚举的策略模式简单了很多,调用方也也不用修改,但如果想要正确地使用枚举策略模式需要额外考虑以下几点。
为了更好的解决系统的扩展性和可维护性,推荐使用 spring 自带 beanFactory 的优势,实现一个基于工厂的策略模式。策略类改动只是添加 @Service 注解,并指定了 Service 的 value 属性即可。
@Service("add")
public class OperationAdd implements Strategy {
@Override
public int doOperation(int num1, int num2) {
return num1 + num2;
}
}
@Service("multiply")
public class OperationMultiply implements Strategy{
@Override
public int doOperation(int num1, int num2) {
return num1 * num2;
}
}
@Service("subtract")
public class OperationSubtract implements Strategy{
@Override
public int doOperation(int num1, int num2) {
return num1 - num2;
}
}
调用类借助 spring 工厂特性,完成策略类
@Slf4j
@RunWith(SpringRunner.class)
@SpringBootTest
public class AppDemo {
/**
* 利用注解 @Resource 和 @Autowired 特性,直接获取所有策略类
*
* key = @Service的value
*/
@Resource
private Map operationMap;
@Test
public void demo() {
String type = "add";
// 获得策略实例
Strategy strategy = operationMap.get(type);
// 执行策略
int result = strategy.doOperation(4, 4);
System.out.println(result);
}
}
工厂策略模式比枚举策略模式繁琐一些,但也更加灵活、易扩展性和易维护。
简单策略推荐使用枚举策略模式,复杂策略推荐工厂策略模式。