Android设计模式系列(11)--SDK源码之策略模式

十度 Android 2015年12月01日 收藏

策略模式其实特别简单(听到这句话,大家是不是心里一下子放松了?)。
比如排序,官方告诉大家我这里有一个排序的接口ISort的sort()方法,然后民间各尽其能,实现这个排序的方法:冒泡,快速,堆等等。
这些方法就是“不同的策略”。
然后,某个模块下,需要一个排序方法,但是暂时不能指定具体的sort方法(出于扩展的考虑),就需要使用ISort接口了。
最后,具体什么场景下,传入什么具体的sort方法,实现灵活的排序。
这就是策略模式!
下面,我们分析Android中的动画是如何使用策略模式的。

1. 意图
定义一系列的算法,把它们一个个封装起来,并且使它们可互相替换。
策略模式使得算法可独立于使用它的客户而变化。

2. 结构图和代码
Animation不同动画的实现,主要是依靠Interpolator的不同实现而变。

定义接口Interpolator:

  1. package android.animation;
  2.  
  3. /**
  4. * A time interpolator defines the rate of change of an animation. This allows animations
  5. * to have non-linear motion, such as acceleration and deceleration.
  6. */
  7. public interface Interpolator {
  8.  
  9. /**
  10. * Maps a value representing the elapsed fraction of an animation to a value that represents
  11. * the interpolated fraction. This interpolated value is then multiplied by the change in
  12. * value of an animation to derive the animated value at the current elapsed animation time.
  13. *
  14. * @param input A value between 0 and 1.0 indicating our current point
  15. * in the animation where 0 represents the start and 1.0 represents
  16. * the end
  17. * @return The interpolation value. This value can be more than 1.0 for
  18. * interpolators which overshoot their targets, or less than 0 for
  19. * interpolators that undershoot their targets.
  20. */
  21. float getInterpolation(float input);
  22. }

我们以AccelerateInterpolator为例,实现具体的策略,代码如下:

  1. package android.view.animation;
  2.  
  3. import android.content.Context;
  4. import android.content.res.TypedArray;
  5. import android.util.AttributeSet;
  6.  
  7. /**
  8. * An interpolator where the rate of change starts out slowly and
  9. * and then accelerates.
  10. *
  11. */
  12. public class AccelerateInterpolator implements Interpolator {
  13. private final float mFactor;
  14. private final double mDoubleFactor;
  15.  
  16. public AccelerateInterpolator() {
  17. mFactor = 1.0f;
  18. mDoubleFactor = 2.0;
  19. }
  20.  
  21. /**
  22. * Constructor
  23. *
  24. * @param factor Degree to which the animation should be eased. Seting
  25. * factor to 1.0f produces a y=x^2 parabola. Increasing factor above
  26. * 1.0f exaggerates the ease-in effect (i.e., it starts even
  27. * slower and ends evens faster)
  28. */
  29. public AccelerateInterpolator(float factor) {
  30. mFactor = factor;
  31. mDoubleFactor = 2 * mFactor;
  32. }
  33.  
  34. public AccelerateInterpolator(Context context, AttributeSet attrs) {
  35. TypedArray a =
  36. context.obtainStyledAttributes(attrs, com.android.internal.R.styleable.AccelerateInterpolator);
  37.  
  38. mFactor = a.getFloat(com.android.internal.R.styleable.AccelerateInterpolator_factor, 1.0f);
  39. mDoubleFactor = 2 * mFactor;
  40.  
  41. a.recycle();
  42. }
  43.  
  44. public float getInterpolation(float input) {
  45. if (mFactor == 1.0f) {
  46. return input * input;
  47. } else {
  48. return (float)Math.pow(input, mDoubleFactor);
  49. }
  50. }
  51. }

其他的Interpolator实现在此不列举了。
如何在Animation模块实现不同的动画呢?
在这里我想提一个应用很广的概念:依赖注入。
在Animation模块里实现不同的动画,就是需要我们把各个Interpolator以父类或者接口的形式注入进去。
注入的方法一般是构造函数,set方法,注释等等。
我们看看animation类是怎么做的:

  1. public abstract class Animation implements Cloneable {
  2. Interpolator mInterpolator;
  3. // 通过set方法注入
  4. public void setInterpolator(Interpolator i) {
  5. mInterpolator = i;
  6. }
  7.  
  8. public boolean getTransformation(long currentTime, Transformation outTransformation) {
  9. // ... ...
  10. // 具体调用
  11. final float interpolatedTime = mInterpolator.getInterpolation(normalizedTime);
  12. applyTransformation(interpolatedTime, outTransformation);
  13. // ... ...
  14. }
  15.  
  16. // 缺省实现,是个小技巧,顺便提下,这个不是重点
  17. protected void ensureInterpolator() {
  18. if (mInterpolator == null) {
  19. mInterpolator = new AccelerateDecelerateInterpolator();
  20. }
  21. }
  22.  
  23. }

  策略模式其实就是多态的一个淋漓精致的体现。

3. 效果
(1).行为型模式
(2).消除了一些if...else...的条件语句
(3).客户可以对实现进行选择,但是客户必须要了解这个不同策略的实现(这句话好像是废话,总而言之,客户需要学习成本)
(4).代码注释中提到了缺省实现,可以让客户不了解策略,也能实现默认的策略
(5).注入的方式有多种:构造函数,set方法,注释。配置解析等等