前言
策略模式属于对象的行为模式。其用意是针对一组算法,将每一个算法封装到具有共同接口的独立的类中,从而使得它们可以相互替换。策略模式使得算法可以在不影响到客户端的情况下发生变化。
背景
在软件开发中常常遇到这种情况,实现某一个功能有多种算法或者策略,我们可以根据环境或者条件的不同选择不同的算法或者策略来完成该功能。如查找、排序等,一种常用的方法是硬编码(Hard Coding)在一个类中,如需要提供多种查找算法,可以将这些算法写到一个类中,在该类中提供多个方法,每一个方法对应一个具体的查找算法;当然也可以将这些查找算法封装在一个统一的方法中,通过if…else…或者case等条件判断语句来进行选择。
这两种实现方法我们都可以称之为硬编码,如果需要增加一种新的查找算法,需要修改封装算法类的源代码;更换查找算法,也需要修改客户端调用代码。在这个算法类中封装了大量查找算法,该类代码将较复杂,维护较为困难。如果我们将这些策略包含在客户端,这种做法更不可取,将导致客户端程序庞大而且难以维护,如果存在大量可供选择的算法时问题将变得更加严重。
问题
如何让算法和对象分开来,使得算法可以独立于使用它的客户而变化?
方案
把一个类中经常改变或者将来可能改变的部分提取出来,作为一个接口,然后在类中包含这个对象的实例,这样类的实例在运行时就可以随意调用实现了这个接口的类的行为。
比如定义一系列的算法,把每一个算法封装起来, 并且使它们可相互替换,使得算法可独立于使用它的客户而变化。这就是策略模式。
适用情况
许多相关的类仅仅是行为有异。 “策略”提供了一种用多个行为中的一个行为来配置一个类的方法。即一个系统需要动态地在几种算法中选择一种。
当一个应用程序需要实现一种特定的服务或者功能,而且该程序有多种实现方式时使用。
一个类定义了多种行为 , 并且这些行为在这个类的操作中以多个条件语句的形式出现。将相关的条件分支移入它们各自的Strategy类中以代替这些条件语句。、
优点
- 可以动态的改变对象的行为
- 策略模式提供了管理相关的算法族的办法。策略类的等级结构定义了一个算法或行为族。恰当使用继承可以把公共的代码移到父类里面,从而避免代码重复。
- 使用策略模式可以避免使用多重条件(if-else)语句。多重条件语句不易维护,它把采取哪一种算法或采取哪一种行为的逻辑与算法或行为的逻辑混合在一起,统统列在一个多重条件语句里面,比使用继承的办法还要原始和落后。
缺点
- 客户端必须知道所有的策略类,并自行决定使用哪一个策略类
- 策略模式将造成产生很多策略类
组成
- 环境类(Context):用一个ConcreteStrategy对象来配置。维护一个对Strategy对象的引用。可定义一个接口来让Strategy访问它的数据。
- 抽象策略类(Strategy):定义所有支持的算法的公共接口。 Context使用这个接口来调用某ConcreteStrategy定义的算法。
- 具体策略类(ConcreteStrategy):以Strategy接口实现某具体算法。
应用
模拟一下城市交通系统,假设情况如下:
公交:起步2元,超过5公里每公里加1元。
地铁:5公里内3元,5-10公里4元,最多5元。
出租:每公里2元。
先声明一个接口,声明一个计算的方法
public interface CalculateStrategy {
int calculatePrice(int km);
}
实现三个具体的计算方法
public class BusCalculate implements CalculateStrategy {
@Override
public int calculatePrice(int km) {
int busP = 2;
if (km>5)
busP = busP+km-5;
return busP;
}
}
public class SubwayCalculate implements CalculateStrategy {
@Override
public int calculatePrice(int km) {
int subwayP=0;
if (km<5)
subwayP = 3;
if (km>5&&km<=10)
subwayP=4;
if (km>10)
subwayP=5;
return subwayP;
}
}
public class TaxiCalculate implements CalculateStrategy {
@Override
public int calculatePrice(int km) {
return 2 * km;
}
}
然后看看现在的计算类,这个是重点
public class PriceCalculate2 {
private CalculateStrategy calculateStrategy;
public void setCalculateStrategy(CalculateStrategy calculateStrategy){
this.calculateStrategy=calculateStrategy;
}
public int getPrice(int km){
return calculateStrategy.calculatePrice(km);
}
}
客户端调用
public class Main {
public static void main(String[] args) {
PriceCalculate2 priceCalculate2 = new PriceCalculate2();
priceCalculate2.setCalculateStrategy(new SubwayCalculate());
int price = priceCalculate2.getPrice(10);
System.out.println(price);
}
}
这里重点就是计算类了,可以看到里面的解法非常简便。而且如果要新增其他算法的话,直接实现一个接口就可以了,其他类都不需要变化。便于之后的升级或扩展。
Android源码中的策略模式
看一下一般使用属相动画时候的样子:
Animation animation = new AlphaAnimation(1,0);
animation.setInterpolator(new AccelerateDecelerateInterpolator());
imageView.setAnimation(animation);
animation.start();
animation.setInterpolator()其实用的就是策略模式,Android中有很多不同的插值器,都实现了Interpolator接口。
然后通过setInterpolator设置给Animation。
在动画执行的时候,会通过设置的计时器来在不同的动画时间执行不同的动画效果。
public void setInterpolator(Interpolator i) {
mInterpolator = i;
}
protected void ensureInterpolator() {
if (mInterpolator == null) {
//默认就是加速度插值器,在Animation初始化的时候执行。
mInterpolator = new AccelerateDecelerateInterpolator();
}
}
public boolean getTransformation(long currentTime, Transformation outTransformation) {
......
if ((normalizedTime >= 0.0f || mFillBefore) && (normalizedTime <= 1.0f || mFillAfter)) {
......
//通过策略模式来获取不同的值。
final float interpolatedTime = mInterpolator.getInterpolation(normalizedTime);
applyTransformation(interpolatedTime, outTransformation);
}
......
}
这里写图片描述
在动画执行过程中,我们需要一些动态效果,有点类似电影的慢镜头,有时需要慢一点,有时需要快一点,这样动画变得灵动起来,这些动态效果就是通过插值器(TimeInterpolator)实现的,我们只需要对Animation对象设置不同的插值器就可以实现不同的动态效果,该效果则使用的就是策略模式实现。
- AccelerateDecelerateInterpolator 在动画开始与介绍的地方速率改变比较慢,在中间的时候加速
- AccelerateInterpolator 在动画开始的地方速率改变比较慢,然后开始加速
- AnticipateInterpolator 开始的时候向后然后向前甩
- AnticipateOvershootInterpolator 开始的时候向后然后向前甩一定值后返回最后的值
- BounceInterpolator 动画结束的时候弹起
- CycleInterpolator 动画循环播放特定的次数,速率改变沿着正弦曲线
- DecelerateInterpolator 在动画开始的地方快然后慢
- LinearInterpolator 以常量速率改变
- OvershootInterpolator 向前甩一定值后再回到原来位置
- PathInterpolator 路径插值器
总结
策略模式的重心
策略模式的重心不是如何实现算法,而是如何组织、调用这些算法,从而让程序结构更灵活,具有更好的维护性和扩展性。
算法的平等性
策略模式一个很大的特点就是各个策略算法的平等性。对于一系列具体的策略算法,大家的地位是完全一样的,正因为这个平等性,才能实现算法之间可以相互替换。所有的策略算法在实现上也是相互独立的,相互之间是没有依赖的。
所以可以这样描述这一系列策略算法:策略算法是相同行为的不同实现。
运行时策略的唯一性
运行期间,策略模式在每一个时刻只能使用一个具体的策略实现对象,虽然可以动态地在不同的策略实现中切换,但是同时只能使用一个。
公有的行为
经常见到的是,所有的具体策略类都有一些公有的行为。这时候,就应当把这些公有的行为放到共同的抽象策略角色Strategy类里面。当然这时候抽象策略角色必须要用Java抽象类实现,而不能使用接口。
这其实也是典型的将代码向继承等级结构的上方集中的标准做法。
end :策略模式主要就是为了分离算法和使用,是系统用于很好的拓展性
网友评论