策略模式(Strategy Pattern)是一种比较简单的模式,也叫做政策模式(Policy Pattern)。其定义如下:定义一组算法,将每个算法都封装起来,并且使它们之间可以互换。(Define a family of algorithms,encapsulate each one,and make them interchangeable.) 策略模式的通用类图如下:  - Context 封装策略模块,屏蔽高层模块对策略、算法的直接访问,封装可能存在的变化,起承上启下作用, - Strategy 策略、算法家族的抽象,通常为接口,定义每个策略或算法必须具有的方法和属性。 - ConcreteStrategy 具体策略角色:实现抽象策略中的操作,该类含有具体的算法。 举个例子,在比赛中,教练准备多个战术,会根据对手的不同,在比赛的不同时段,安排使用,举个例子: ```java interface IStrategy { // 每个策略都是一个可执行的算法 public void operate(); } class FirstHalfStrategy implements IStrategy { public void operate() { System.out.println("1. 快速突击,抢占开局"); System.out.println("2. 内外线配合,多点进攻"); } } class SecondHalfStrategy implements IStrategy { public void operate() { System.out.println("1. 快速突击,巩固优势"); System.out.println("3. 注意控制犯规"); } } class EqualizeStrategy implements IStrategy { public void operate() { System.out.println("1. 保护前场篮板"); System.out.println("2. 锋线突破分球"); System.out.println("3. 外线主攻"); } } ``` 上面的每个战术都可以被认为是一个策略,比赛开始后,交给场上队长将安排队员给队员执行,Caption 类就是封装策略的 Context 。 > Caption 类借用了代理模式的思路,不同的是 Caption 类不是 IStrategy 的接口实现,Caption 中 has a IStrategy 对象; > 如果使用代理模式实现 Context 类(让 Context 实现 IStrategy 接口),那么 Caption is a IStrategy 对象。 ```java class Caption { private IStrategy strategy; // 加载策略 public Context(IStrategy strategy) { this.strategy = strategy; } // 执行策略 public void operate() { this.strategy.operate(); } } ``` 在场景中,教练根据情况将策略拿出来,交给场上队长执行: ```java public class App { public static void main(String[] args) throws Exception { Caption caption; // 上半场执行策略 1 System.out.println("---上半场执行策略 1---"); context = new Context(new FirstHalfStrategy()); context.operate(); System.out.println("\n\n"); // 下半场执行策略 2 System.out.println("---下半场执行策略 2---"); context = new Context(new SecondHalfStrategy()); context.operate(); System.out.println("\n\n"); // 被连续打追身,执行策略 3 System.out.println("---被连续打追身,执行策略 3---"); context = new Context(new EqualizeStrategy()); context.operate(); System.out.println("\n\n"); } } ``` 有一些教练,不知道是习惯躺平,还是喜欢将球员培养成教练,不管怎样,将策略一股脑告诉队员,比赛开始后,让球员根据情况自行选择,这就引出了策略模式的一个变体:策略枚举: 例如:通过在枚举成员中封装号策略对象,然后在 exec 方法中通过 IStrategy 接口来执行每个策略: ```java enum StrategyPackage { Package1(new FirstHalfStrategy()), Package2(new SecondHalfStrategy()), Package3(new EqualizeStrategy()); IStrategy strategy = null; private StrategyPackage(IStrategy _strategy) { this.strategy = _strategy; } // 声明一个抽象函数 public void exec() { this.strategy.operate(); } } public class App { public static void main(String[] args) throws Exception { // 上半场执行策略 1 System.out.println("---上半场执行策略 1---"); StrategyPackage.Package1.exec(); System.out.println("\n\n"); // 下半场执行策略 2 System.out.println("---下半场执行策略 2---"); StrategyPackage.Package2.exec(); System.out.println("\n\n"); // 被连续打追身,执行策略 3 System.out.println("---被连续打追身,执行策略 3---"); StrategyPackage.Package3.exec(); System.out.println("\n\n"); } } ``` 总结一下,策略枚举定义如下:它是一个浓缩了的策略模式的枚举。 相比策略模式,策略枚举的问题在于扩展新的策略,在枚举类中添加新的枚举成员,可扩展性不好,但好处在于调用方式直观,代码易于阅读。 ## 策略模式的优点 算法可以自由切换,避免使用多重条件判断,扩展性良好 ## 策略模式的缺点 复用的可能性很小,策略类数量增多 所有的策略类都需要对外暴露,虽然策略枚举解决了这个问题,但是牺牲了可扩展性。 ## 策略模式的使用场景 多个类只有在算法或行为上稍有不同的场景。 算法需要自由切换的场景。 需要屏蔽算法规则的场景,现在的科技发展得很快,人脑的记忆是有限的(就目前来说是有限的),太多的算法你只要知道一个名字就可以了,传递相关的数字进来,反馈一个运算结果,万事大吉。 ## 策略模式的注意事项 如果系统中的一个策略家族的具体策略数量超过4个,则需要考虑使用混合模式,解决 策略类膨胀和对外暴露的问题,否则日后的系统维护就会成为一个烫手山芋,谁都不想接。
×
Sign in
Email
Password
Forgot password
or
By clicking below, you agree to our
terms of service
.
Sign in via Facebook
Sign in via Twitter
Sign in via GitHub
Sign in via Dropbox
Sign in with Wallet
Wallet (
)
Connect another wallet
New to HackMD?
Sign up