1.1概述
方法是类中最重要的组成部分,一个方法的方法体由一系列语句构成,也就是说一个方法的方法体是一个算法。在某些设计中,一个类的设计人员经常可能涉及这样的问题:由于用户需求的变化,导致经常需要修改类中某个方法的方法体,即需要不断地变化算法。在这样的情况下可以考虑使用策略模式。
策略模式是处理算法不同变体的一种成熟模式,策略模式通过接口或抽象类封装算法的标识,即在接口中定义一个抽象方法,实现该接口的类将实现接口中的抽象方法。策略模式把针对一个算法标识的一系列具体算法分别封装在不同的类中,使得各个类给出的具体算法可以相互替换。
在策略模式中,封装算法标识的接口称作策略,实现该接口的类称作具体策略。
1.2模式的结构
策略模式的结构包括三种角色:
(1)策略(Strategy):策略是一个接口,该接口定义若干个算法标识,即定义了若干个抽象方法。
(2)具体策略(ConcreteStrategy):具体策略是实现策略接口的类。具体策略实现策略接口所定义的抽象方法,即给出算法标识的具体算法。
(3)上下文(Context):上下文是依赖于策略接口的类,即上下文包含有策略声明的变量。上下文中提供了一个方法,该方法委托策略变量调用具体策略所实现的策略接口中的方法。
策略模式接口的类图如下所示:
1.3策略模式的优点
(1)上下文和具体策略是松耦合关系。因此上下文只知道它要使用某一个实现Strategy接口类的实例,但不需要知道具体是哪一个类。
(2)策略模式满足“开-闭原则”。当增加新的具体策略时,不需要修改上下文类的代码,上下文就可以引用新的具体策略的实例。
1.4适合使用策略模式的情景
(1)一个类定义了多种行为,并且这些行为在这个类的方法中以多个条件语句的形式出现,那么可以使用策略模式在类中使用大量的条件语句。
(2)程序不希望暴露复杂的、与算法有关的数据结构,那么可以使用策略模式来封装算法。
(3)需要使用一个算法的不同变体。
1.5策略模式的使用
下面通过一个简单的实例,实现1.1概述中简单例子:在某种比赛中有若干个裁判,每位裁判给选手一个得分。选手的最后得分是根据全体裁判的得分计算出来的。请给出几种计算选手得分的评分方案(策略),对于某次 比赛,可以从你的方案中选择一种方案作为本次比赛的评分方案。
针对上述问题,使用策略模式设计若干个类。具体如下:
首先看一下本实例构建框架具体类和1.2模式的结构中类图的对应关系,如下图所示:
(1)策略(Strategy)
本问题中,策略接口的名字是ComputableStrategy,该接口规定的算法标识即抽象方法是double computeScore(double []a),ComputableStrategy接口的代码如下:
package com.liuzhen.four_strategy; public interface ComputableStrategy { public abstract double computeScore(double[] a); }
(2)具体策略
对于本问题,有三个具体策略StrategyOne,StrategyTwo和StrategyTree。其中StrategyOne类实现为计算数组a的元素代数平均值;StrategyTwo类实现计算数组a的元素的几何平均值;StrategyThree类实现为去掉数组a中最大元素和最小元素 ,然后计算剩余元素的代数平均值。
StrategyOne类代码如下:
package com.liuzhen.four_strategy; public class StrategyOne implements ComputableStrategy{ public double computeScore(double[] a){ double score = 0 , sum = 0; for(int i = 0;i < a.length;i++){ sum += a[i]; } score = sum/a.length; return score; } }
StrategyTwo类代码如下:
package com.liuzhen.four_strategy; public class StrategyTwo implements ComputableStrategy { public double computeScore(double[] a) { double score = 0 , multi = 1; int n = a.length; for(int i = 0;i < a.length;i++){ multi = multi*a[i]; } score = Math.pow(multi,1.0/n); return score; } }
StrategyThree类代码如下:
package com.liuzhen.four_strategy; import java.util.Arrays; public class StrategyThree implements ComputableStrategy{ public double computeScore(double[] a){ if(a.length <= 2) return 0; double score = 0,sum = 0; Arrays.sort(a); for(int i = 1;i < a.length-1;i++){ sum += a[i]; } score = sum/(a.length-2); return score; } }
(3)上下文
上下文是GymnasticsGame类,该类包含策略声明的变量,此变量可用于保存具体策略的引用。另外,GymnasticsGame类中包含一个double型数组a,a的元素代表各个裁判给选手的评分。该类中的getPersonScore(double a[])方法将委托具体策略的实例计算选手的最后得分。GymnasticsGame类的代码如下:
package com.liuzhen.four_strategy; public class GymnasticsGame { ComputableStrategy strategy; public void setStrategy(ComputableStrategy strategy){ this.strategy = strategy; } public double getPersonScore(double[] a){ if(strategy != null) return strategy.computeScore(a); else return 0; } }
(4)具体使用
package com.liuzhen.four_strategy; public class FourApplication { public static void main(String args[]){ GymnasticsGame game = new GymnasticsGame(); //上下文对象 game.setStrategy(new StrategyOne()); //上下文对象使用策略一 Person zhang = new Person(); zhang.setName("张三"); double[] a = {9.12,9.25,8.87,9.99,6.99,7.88}; Person li = new Person(); li.setName("李四"); double[] b = {9.15,9.26,8.97,9.89,6.97,7.89}; zhang.setScore(game.getPersonScore(a)); li.setScore(game.getPersonScore(b)); System.out.printf("%s最后得分:%5.3f%n",zhang.getName(),zhang.getScore()); System.out.printf("%s最后得分:%5.3f%n",li.getName(),li.getScore()); game.setStrategy(new StrategyTwo()); //上下文对象使用策略二 zhang.setScore(game.getPersonScore(a)); li.setScore(game.getPersonScore(b)); System.out.println("使用几何平均值方案:"); System.out.printf("%s最后得分:%5.3f%n",zhang.getName(),zhang.getScore()); System.out.printf("%s最后得分:%5.3f%n",li.getName(),li.getScore()); game.setStrategy(new StrategyThree()); //上下文对象使用策略三 zhang.setScore(game.getPersonScore(a)); li.setScore(game.getPersonScore(b)); System.out.println("使用(去掉最大值和最小值)算术平均值方案:"); System.out.printf("%s最后得分:%5.3f%n",zhang.getName(),zhang.getScore()); System.out.printf("%s最后得分:%5.3f%n",li.getName(),li.getScore()); } }
其中,Person类代码如下:
package com.liuzhen.four_strategy; public class Person { String name; double score; public void setScore(double score){ this.score = score; } public void setName(String name){ this.name = name; } public double getScore(){ return score; } public String getName(){ return name; } }
运行结果如下:
张三最后得分:8.683 李四最后得分:8.688 使用几何平均值方案: 张三最后得分:8.625 李四最后得分:8.631 使用(去掉最大值和最小值)算术平均值方案: 张三最后得分:8.780 李四最后得分:8.817
参考资料:
1.Java设计模式/耿祥义,张跃平著.——北京:清华大学出版社,2009.5