23种设计模式之策略模式
参考资料
- Java设计模式:23种设计模式全面解析(超级详细)
- 韩顺平老师的Java设计模式(图解+框架源码剖析)
- 秦小波老师的《设计模式之禅》
下文如有错漏之处,敬请指正
一、简介
定义
定义了一系列算法,并将每个算法都封装起来,使它们之间可以相互替换。
特点
- 策略模式是一种行为型模式
- 策略模式类似于一个可反复拆卸的插件
通用类图
策略模式的主要角色:
-
Context
环境角色
持有一个抽象策略角色的引用,起承上启下封装作用,屏蔽高层模块对策略、算法的直接访问,封装可能存在的变化。 -
Strategy
抽象策略角色
策略、算法家族的抽象,通常为接口,各种不同的算法以不同的方式实现这个接口,环境角色使用这个接口调用不同的算法。 -
ConcreteStrategy
具体策略角色
实现了抽象策略中的操作,提供具体的算法实现。
优点
-
避免使用多重条件判断
多重条件语句不易维护,使用策略模式可以避免使用多重条件语句。 -
扩展性良好
策略模式提供了对开闭原则的完美支持,可以在不修改原代码的情况下,灵活增加新算法。
缺点
-
所有的策略类都需要对外暴露
上层模块必须知道有哪些策略,然后才能决定使用哪一个策略,这与迪米特法则是相违背的,我只是想使用了一个策略,我凭什么就要了解这个策略呢?那要你的封装类还有什么意义?这是原装策略模式的一个缺点,幸运的是,我们可以使用其他模式来修正这个缺陷,如工厂方法模式、代理模式或享元模式。 -
策略类数量膨胀
每一个策略都是一个类,复用的可能性很小,类数量增多。
应用场景
- JDK 中Arrays.sort排序方法,可以使用Comparator自定义排序规则,Comparator就是一个策略。
- 一个系统需要动态地在几种算法中选择一种时,可将每个算法封装到策略类中。
- 系统中各算法彼此完全独立,且要求对客户隐藏具体算法的实现细节。
二、策略模式
需求:
有一个攻击技能,普通玩家攻击伤害10点,VIP玩家每次攻击伤害50点,超级VIP玩家每次攻击100点,使用策略模式实现该功能。
抽象策略:
package strategy;
public interface Strategy {
// 策略模式的运算法则
public void attack();
}
具体策略:
package strategy;
public class GeneralStrategy implements Strategy {
// 具体策略的运算法则
@Override
public void attack() {
System.out.println("普通玩家,攻击10点");
}
}
package strategy;
public class VIPStrategy implements Strategy {
// 具体策略的运算法则
@Override
public void attack() {
System.out.println("VIP玩家,攻击50点");
}
}
package strategy;
public class SupremeStrategy implements Strategy {
// 具体策略的运算法则
@Override
public void attack() {
System.out.println("至尊玩家,攻击100点");
}
}
环境:
package strategy;
public class Context {
// 抽象策略
private Strategy strategy = null;
// 空参构造器
public Context() {
}
// 构造函数设置具体策略
public Context(Strategy strategy) {
this.strategy = strategy;
}
// 设置具体策略
public void setStrategy(Strategy strategy) {
this.strategy = strategy;
}
// 封装后的策略方法
public void attack() {
strategy.attack();
}
}
Client:
package strategy;
public class Client {
public static void main(String[] args) {
// 实例化环境
Context context = new Context();
// 模拟普通玩家
context.setStrategy(new GeneralStrategy());
// 执行封装后的方法
context.attack();
// 模拟VIP玩家
context.setStrategy(new VIPStrategy());
// 执行封装后的方法
context.attack();
// 模拟至尊玩家
context.setStrategy(new SupremeStrategy());
// 执行封装后的方法
context.attack();
/**
* 输出结果:
* 普通玩家,攻击10点
* VIP玩家,攻击50点
* 至尊玩家,攻击100点
*/
}
}
当出现更高级的玩家时,可以非常方便的增加一个相应的攻击策略(子类实现抽象策略类),而不改变原有代码结构。
三、总结
策略模式是一个非常简单的模式。它在项目中使用得非常多,但它单独使用的地方就比较少了,因为它有致命缺陷:所有的策略都需要暴露出去,这样才方便客户端决定使用哪一个策略。在实际项目中,我们一般通过工厂方法模式来实现策略类的声明,从而避免暴露策略类。