1.定义
在策略模式(Strategy Pattern)中,一个类的行为或其算法可以在运行时更改。这种类型的设计模式属于行为型模式。
在策略模式中,我们创建表示各种策略的对象和一个行为随着策略对象改变而改变的 context 对象。策略对象改变 context 对象的执行算法。
2.概述
方法是类中最重要的组成部分,一个方法的方法体由一系列语句组成,也就是说一个方法的方法体是一个算法。在某些设计中,一个类的设计人员经常可能设计这样的问题:由于用户需求的变化,导致经常需要修改类中某个方法的方法体,急需要不断地变化算法;
比如,设计Army类(士兵类),该类中有一个int数组,数组元素的值代表士兵的号码,该类中有lineUp()方法,该方法将士兵按它们的号码从小到大排队,类图如下所示:
但是有些部队希望Army创建的对象能够将士兵按照他们的号码从大到小排队或者按照某种方式排序,而不是从小到大。显然Army无法提供这样的对象;
面对这种情况需求,我们只好痛苦地修改lineUp()的方法体,但马上就会发现不行,因为一旦将方法体改变,我们又无法满足以前的业务需求;也许可以在方法中添加多重条件语句,以便根据用户的具体需求决定怎么样排队,但这也不是一个好办法,因为只要一旦有新的需求,就要修改lineUp方法添加判断语句,而且针对某个条件语句的排队代码也可能因该用户的需求变化导致重新编写;
因此,我们采用面向抽象编程来解决该类问题;
3.应用场景
1、如果在一个系统里面有许多类,它们之间的区别仅在于它们的行为,那么使用策略模式可以动态地让一个对象在许多行为中选择一种行为。
2、一个系统需要动态地在几种算法中选择一种。
3、如果一个对象有很多的行为,如果不用恰当的模式,这些行为就只好使用多重的条件选择语句来实现。
4.模式的结构与使用
策略模式的结构中包括三种角色
1.策略(Strategy):策略就是一个接口,该接口定义若干个算法标识,即定义了若干个抽象方法;
2.具体策略(ConcreteStrategy):具体策略是实现策略接口的类。具体策略实现策略接口所定义的抽象方法,即给出算法标识的具体实现;
3.上下文(Context):上下文是依赖于策略接口的类,即上下文包含有策略声明的变量。上下文中提供一个方法,该方法委托策略变量调用具体策略所实现的策略接口;
1.策略模式的UML类图
2.结构的描述
以下通过一个简单的问题来描述策略模式所涉及的各个角色
在某种比赛中有若干个裁判,每位裁判给选手一个得分。选手的最后得分是根据全体裁判的得分计算出来的。请给出集中计算选手得分的评分方法(策略),对于某次比赛,可以从你的方案中选择一种方法作为本次比赛的评分方案。
针对上述问题,使用策略模式设计若干个类。
1.策略
定义了一个策略接口,名字为ComputableStrategy,在里面定义了抽象方法computeScore;
package com.xing.strategy;
public interface ComputableStrategy {
public abstract double computeScore(double[] a);
}
2.具体策略
对于本问题,有三个具体略:StrategyOne,StrategyTwe,StrategyThree;
具体策略StrategyOne将 double computeScore(double[] a)方法实现为计算数组a的元素的代数平均值,具体代码如下所示:
package com.xing.strategy;
public class StrategyOne implements ComputableStrategy{
//计算数组a的元素的代数平均值
@Override
public double computeScore(double[] a) {
int sum=0;
int score=0;
for (double value : a) {
sum+=value;
}
score=sum/a.length;
return score;
}
}
具体策略StrategyTwe将 double computeScore(double[] a)方法实现为计算数组a的元素的几何平均值,具体代码如下所示:
package com.xing.strategy;
public class StrategyTwo implements ComputableStrategy{
//计算数组a的元素的几何平均值
@Override
public double computeScore(double[] a) {
double sum=0;
double score=0;
for (int i = 0; i < a.length; i++) {
sum=sum*a[i];
}
score=Math.pow(sum,1.0/a.length);
return score;
}
}
具体策略StrategyThree将 double computeScore(double[] a)方法实现为去掉数组a的元素中的一个最大值和一个最小值,然后计算剩余元素的代数平均值,具体代码如下所示:
package com.xing.strategy;
import java.util.Arrays;
public class StrategyThree implements ComputableStrategy{
//去掉数组a的元素中的一个最大值和一个最小值,然后计算剩余元素的代数平均值
@Override
public double computeScore(double[] a) {
if(a.length<=2)
return 0;
double sum=0;
double score=0;
//对数组进行从小到大排序
Arrays.sort(a);
for(int i=1;i<a.length-1;i++){
sum+=a[i];
}
score=sum/(a.length-2);
return score;
}
}
3.上下文
上下文是Gymnastics类,该类包含策略声明的变量,此变量可用于保存具体策略的引用。该类中的getPersonScore(double[] a)方法将委托具体策略的实例计算选手的最后得分,具体代码如下所示:
package com.xing.strategy;
public class Gymnastics {
ComputableStrategy computableStrategy;
public void setComputableStrategy(ComputableStrategy computableStrategy) {
this.computableStrategy = computableStrategy;
}
public double getPersonScore(double[] a) {
if (computableStrategy != null) {
return computableStrategy.computeScore(a);
} else
return 0;
}
}
4.主程序测试
package com.xing.strategy;
public class Application {
public static void main(String[] args) {
//创建上下文对象
Gymnastics game=new Gymnastics();
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.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 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());
}
}
class Person{
String name;
double score;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getScore() {
return score;
}
public void setScore(double score) {
this.score = score;
}
}
5.测试结果展示
5.策略模式的优点。
1.上下文(Context)和具体策略(ConcreteStrategy)是松耦合关系。因此上下文只知道它要使用某一个实现Strategy接口类的实例,但不需要知道具体是哪一个类;
2.策略模式满足开闭原则。当增加新的具体策略时,不需要需求上下文类的代码,上下文就可以引用新的具体策略的实例;