引入
书接上回,我们来聊聊啥是策略模式?
这是一个将算法封装起来的模式,然后提供一个set函数来让它们可以相互替换,这个模式让算法可独立于使用它的客户而变化。
适合用策略模式的场景
- 一个类有很多种行为,使用这个模式可以避免在类中使用大量的条件语句(不同的用户调用,不要写成if判断是哪个用户,直接set设置就在同一个接口函数中表现出不一样的行为)
- 程序不希望暴露复杂的、与算法相关的数据结构
- 需要使用一个算法的不同变体
策略模式的定义
定义一系列算法,把它们一个个封装起来,并且使它们可以相互替换。
策略模式的结构
上下文对象中引用这个策略接口,具体是策略继承这个策略接口。上下文只需要向这个策略接口发消息就可以了~
上下文对象
//上下文对象
public class GymnasticsGame {
Computable strategy;//保存策略的引用
//用于设置切换哪个具体策略
public void setStrategy(Computable strategy){
this.strategy = strategy;
}
//委托函数,客户端程序不直接调用策略,而通过上下文对象转发,实现策略对象的共享
public double getPersonScore(double[] a){
if (strategy!=null)
return strategy.computeScore(a);//调用具体策略的方法
else
return 0 ;
}
}
策略接口
//策略接口
public interface Computable {
public abstract double computeScore(double[] a);
}
具体的三种策略
public class StrategyOne implements Computable{
//实现计算数组a的元素的代数平均值
@Override
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;
}
}
public class StrategyTwo implements Computable{
//实现计算数组a的元素的几何平均值
@Override
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;
}
}
public class StrategyThree implements Computable{
//去调一个最大值、一个最小值,实现计算数组a的剩余元素的代数平均值
@Override
public double computeScore(double[] a) {
if(a.length<2)
return 0 ;
double score =0 ,sum = 0;
Arrays.sort(a);
for (int i = 0; i < a.length-1; i++) {
sum = sum +a[i];
}
score = sum/(a.length-2);
return score;
}
}
应用程序
public class Application {
public static void main(String[] args) {
GymnasticsGame game = new GymnasticsGame(); //new一个上下文对象
game.setStrategy(new StrategyOne());//上下文对象设置使用策略一
Person albert = new Person();
albert.setName("albert");
double[] a = {9,12,2,4,64,2,45,6,12};
albert.setScore(game.getPersonScore(a));//赋值语句,这个调用方式就是直接调用上下文对象的getPersonScore函数
System.out.println(albert.getName()+" 使用算术平均值方案, 求出的值为: "+albert.getScore());
game.setStrategy(new StrategyTwo());//重新set上下文的具体策略
albert.setScore(game.getPersonScore(a));
System.out.println(albert.getName()+" 使用几何平均值方案, 求出的值为: "+albert.getScore());
}
//建立一个内部类,完善业务场景逻辑
private static 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;
}
}
}
从这里可以看出来,具体策略对象实例其实是应用程序对象创建的,上下文对象其实并不知道它要向哪个具体策略发消息,这为添加新的具体策略提供的方便;而应用程序本身也不用直接和具体策略对象发生交互,通过上下文对象的消息转发,可以让接口统一,达到一个接口不同表现的目的。
优点
- 上下文对象和具体策略是松耦合关系,上下文只知道它要使用某一个实现strategy接口类的实例,但不需要知道具体是哪一个类
- 策略模式满足开闭原则
策略模式相对于继承机制的优势
策略模式采用的是组合方法,即将一个类的某个方法内容的不同变体分别封装在不同的类中,而该类仅仅依赖这些类所实现的一个共同接口。上下文对象的修改并不会影响到具体策略,这正是组合比继承更有优势的地方
总结
策略模式其实就是将函数方法封装起来的一种模式,通过上下文对象来实现方法的转换,避免了我们要用大量的条件语句来判断使用哪些方法的尴尬,让上下文对象去调用具体策略,应用程序不需要知道算法的细节。
考虑到系统的扩展性和复用性,就要注意面向对象的一个基本原则之一:少用继承,多用组合。