一、定义
定义一些列算法,把它们一个一个封装起来,并且使它们可以相互替换。本模式使得算法可独立于使用它的客户而变化。
二、概述
方法是类中最重要的组成部分,一个方法的方法体是由一些列语句组成,也就是说一个方法的方法体是一个算法。在开发过程中,经常遇到,由于用户需求的变化,需要经常修改类中某个方法的方法体,即需要不断变化算法。比如说排队,就有好几种方式:从小到大、从大到小或者是按照号码排队等等,像这种情况也许可以使用判断语句来针对不同的情况来编写不同的代码。但会造成方法中代码过多且耦合性太强,其实该问题的症结就是,排队的方法中的代码需要经常变化。这时就可以将类中经常需要变化的部分分割出来,并将每种可能的变化对应的交给抽象类的一个子类或实现接口的一个类去负责,避免类依赖于具体的实现。
策略模式就是处理算法不同的变体的一种模式。它包含三个角色:
1、策略(Strategy):策略是一个接口,该接口定义若干的算法标识,即定义了若干个抽象方法。
2、具体策略(ConcreteStrategy):具体策略是实现策略接口的类。具体策略实现策略定义的抽象方法,即给出算法标识的具体算法。
3、上下文(Context):上下文是依赖于算法接口的类,即上下文包含策略声明的变量。
三、举例:
某比赛有若干裁判,选手的最后得分是根据所以裁判的得分计算出来的。现在有三种评分方案(当然还有好多种方案,这里举例三种):
1、根据所有分数的代数平均值
2、根据所有分数的几何平均值
3、减去最大值和最小值后的所有分数的代数平均值
在实际比赛中可以任选一种去进行评分,接下来使用代码实现。
1、策略(Strategy)
是一个接口,规定可算法标识,即抽象方法
public interface ComputableStrategy{
public abstract double computeScore(double[] a);
}
2、具体策略(ConcreteStrategy)
三种评分方案就是具体策略分别命名为:StrategyOne,StrategyTwo,StrategyThree
StrategyOne.jave
public class StrategyOne implements ComputableStrategy{
public double computeScore(double[] a){
double score = 0, sum = 0;
for( int i = 0; i < a.length; i++){
//所有分之和
sum = sum + a[i];
}
//求平均值
score = sum / a.length;
//返回得分
return score;
}
}
StrategyTwo.jave
public class StrategyTwo implements ComputableStrategy{
public double computeScore(double[] a){
double score = 0, multi = 1;
for( int i = 0; i < a.length; i++){
multi = multi * a[i];
}
//几何平均分
score = Math.pow(multi, 1.0 / a.length);
//返回得分
return score;
}
}
StrategyThree.jave
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 = sum + a[i];
}
//平均分
score = sum / a.length;
//返回得分
return score;
}
}
上下文(Context)
该类包含策略的声明,此外会提供一个方法来获得裁判的得分。命名GymnasticsGame
public class GymnasticsGame{
//声明策略对象
ComputableStrategy strategy;
public void setStrategy(ComputableStrategy strategy){
this.setStrategy = setStrategy;
}
public double getPersonScore(double[] a){
if(setStrategy != null){
//返回得分
return setStrategy.computeScore(a);
}else{
return 0;
}
}
}
测试
public class Application{
public static void main(String args[]){
GymnasticsGame game = new GymnasticsGame();
double a = {9.12,9.25,8.87,9.99,6.99,7.88};
//代数平均值方案
game.setStrategy(new StrategyOne());
System.out.println("使用代数平均值方案,最后得分是:" + game.getPersonScore(a));
//几何平均值方案
game.setStrategy(new StrategyTwo());
System.out.println("使用几何平均值方案,最后得分是:" + game.getPersonScore(a));
//减去最大值最小值后的代数平均值方案
game.setStrategy(new StrategyThree());
System.out.println("使用减去最大值最小值后的代数平均值方案,最后得分是:" + game.getPersonScore(a));
}
}
四、优点
1、上下文和具体策略之间是松耦合关系,上下文只知道它是使用某一个Strategy接口的实例,但不知道具体是哪一个类。
2、 策略模式满足“开-闭原则”。当新增具体的策略时,不需要修改上下文的代码,就可以引用新的具体策略实例
五、相对于继承的优势
通过继承也可以改变对象的行为,子类可以重写父类的方法来改变该方法的行为,使得子类对象具有不同于父类的行为。但这种方式就会使得一旦父类需要增加或修改某一方法,那么所有实现父类的子类都需要进行修改,系统的扩展性和复用性就会变差。也不符合面向对象的基本原则“少用继承,多用组合”。而策略模式采用的是组合方法,将一个类中某个方法中不同的变体封装在不同类中,而该类仅仅依靠这些类所实现的一个共同接口。