设计模式之策略模式

171 阅读3分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第18天,点击查看活动详情

策略模式的定义

策略模式定义了一系列的算法,并把每一个算法封装起来,并使它们可以相互替换。策略模式使得这些算法在不影响客户端的情况下发生变化。

策略模式涉及到的角色

  • 环境对象(Context): 持有Strategy的引用。
  • 抽象策略对象(Strategy): 定义所有支持算法的公共接口。
  • 具体策略对象(ConcreteStrategy): 以Strategy接口实现具体算法。

策略模式的实现步骤

  1. 实现抽象策略对象,即对具体策略对象定义一个公共接口。
  2. 编写具体策略类,实现定义的公共接口。
  3. 编写环境角色,保存一个策略对象的引用。
  4. 客户端编写,对环境角色中保存的引用赋值,并调用方法。

具体实现

需求

实现一个排序算法,对一个整型数组array排序。

定义一个接口(抽象策略类)

public interface IStrategy {
    int[] sort(int[] array);
}

实现该接口,并实现具体的算法

这里先实现冒泡排序策略和选择排序策略两种。

/**
 * 冒泡排序策略
 */
public class MaoPaoStrategy implements IStrategy {
    public int[] sort(int[] array) {
        for(int i=0;i<array.length-1;i++){
            for(int j=0;j<array.length-i-1;j++){
                if(array[j]>array[j+1]){
                    int temp = array[j];
                    array[j] = array[j+1];
                    array[j+1] = temp;
                }
            }
        }
        return array;
    }
}
/**
* 选择排序策略
*/
public class SelectStrategy implements IStrategy {
    @Override
    public int[] sort(int[] array) {
      for (int i=0;i < array.length;i++){
          int k=i;//记录最小元素的下标
          for (int j = i+1;j<array.length;j++){
              if (array[k] > array[j]) {
                  k = j;
              }
          }
          int temp = array [i];
          array [i] = array[k];
          array [k] = temp;
      }
      return array;
    }
}

定义环境对象,并保存策略对象的引用

public class Context {
    private IStrategy iStrategy;
    public Context (IStrategy iStrategy){
        this.iStrategy = iStrategy;
    }
    public int[] sort(int[] array){
        return iStrategy.sort(array);
    }
}  

客户端调用,向环境对象传入具体的策略对象

public class Client {
    private static Context context;
    public static void main(String[] args) {
        System.out.println("未排序的数组:"+Arrays.toString(new int[]{75,8,4,63,80,54,6,97,70,99}));;
        context = new Context(new MaoPaoStrategy());
        System.out.println("冒泡排序策略:"+Arrays.toString(context.sort(new int[]{75,8,4,63,80,54,6,97,70,99})));;
        context = new Context(new SelectStrategy());
        System.out.println("选择排序策略:"+Arrays.toString(context.sort(new int[]{75,8,4,63,80,54,6,97,70,99})));
    }
}

策略模式的缺点

  • 客户端必须知道所有的策略类,并决定使用那一个策略类。
  • 每一个具体的策略都对应一个新类,结果会造成很多策略类。

策略模式的优点

  • 可以动态地改变同一行为的不同实现。
  • 每一个策略算法都是相互独立的,地位平等,相互之间没有依赖性。

应用场景

在Android源码中,使用策略模式的场景很多,最典型的就是在属性动画的应用

插值器

在我们使用属性动画的时候,经常设置插值器,他的作用是根据时间的流逝来计算当前属性值改变的比例,我们在设置的时候如果传null就默认使用线性插值器,系统给我们预设了很多插值器,如下

//线性插值器
public class LinearInterpolator extends BaseInterpolator {}
//加速减速插值器
public class AccelerateDecelerateInterpolator extends BaseInterpolator {}
//循环插值器
public class CycleInterpolator extends BaseInterpolator {}

LinearInterpolator,AccelerateDecelerateInterpolator,CycleInterpolator这些类都是具体策略,外部可以通过设置不同的插值器从而实现不同的效果。

估值器

估值器是属性动画中另一个使用策略模式的地方,它和插值器类似,作用是根据当前属性值改变的百分比计算改变之后的属性值,抽象策略是TypeEvaluator接口

public interface TypeEvaluator<T> {
    T evaluate(float fraction, T startValue, T endValue);
}

它有很多实现类,比如IntEvaluator,ArgbEvaluator,FloatEvaluator等

public class IntEvaluator implements TypeEvaluator<Integer> {}
public class ArgbEvaluator implements TypeEvaluator {}
public class FloatEvaluator implements TypeEvaluator<Number> {}