Define a family of algorithms, encapsulate each one, and make them interchangeable. Strategy lets the algorithm vary independently from clients that use it.(定义一系列算法,将每个算法分别封装起来,让它们可以互相替换。策略模式可以使算法的变化独立于使用它们的客户端(这里的客户端代指使用算法的代码)。)
策略模式,最常见的应用场景是,利用它来避免冗长的 if-else 或 switch 分支判断。不过,它的作用还不止如此。它也可以像模板模式那样,提供框架的扩展点等等。
工厂模式 是解耦对象的创建和使用,观察者模式 是解耦观察者和被观察者。策略模式 跟两者类似,也能起到解耦的作用,不过,它解耦的是策略的定义、创建、使用这三部分。
1 策略模式 UML
Context : 用来操作策略的类
Stragety: 策略的抽象
ConcreteStragetyA: 具体的策略实现
策略模式的定义 根据上图可实现如下代码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public interface Strategy { void algorithm () ; } public class ConcreteStrategyA implements Strategy { @Override public void algorithm () { } } public class ConcreteStrategyB implements Strategy { @Override public void algorithm () { } }
策略的创建 这里讲一下操作策略的类,如何创建策略,因为策略模式会包含一组策略,在使用它们的时候,一般会通过类型(type)来判断创建哪个策略来使用。如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public class StrategyFactory { public static Strategy getStrategy (String type) { if (type == null || type.isEmpty()) { throw new IllegalArgumentException("type should not be empty." ); } if (type.equals("A" )) { return new ConcreteStrategyA(); } else if (type.equals("B" )) { return new ConcreteStrategyB(); } return null ; } }
为了封装创建逻辑,我们需要对客户端代码屏蔽创建细节。我们可以把根据 type 创建策略的逻辑抽离出来,放到工厂类中。 如果策略类是无状态的,不包含成员变量,只是纯粹的算法实现,这样的策略对象是可以被共享使用的,不需要在每次调用 getStrategy() 的时候,都创建一个新的策略对象。针对这种情况,我们可以使用如下方式创建,事先创建好每个策略对象,缓存到工厂类中,用的时候直接返回。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public class StrategyFactory { private static final Map<String, Strategy> strategies = new HashMap<>(); static { strategies.put("A" , new ConcreteStrategyA()); strategies.put("B" , new ConcreteStrategyB()); } public static Strategy getStrategy (String type) { if (type == null || type.isEmpty()) { throw new IllegalArgumentException("type should not be empty." ); } return strategies.get(type); } }
如果策略类是有状态的,根据业务场景的需要,我们希望每次从工厂方法中,获得的都是新创建的策略对象,而不是缓存好可共享的策略对象,那就不可以用这种方式,还是得用第一种 if - else 方式。
策略模式案例 案例内容为按距离计算 公交、地铁 最终支付价格。首先先看第一版代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 public class PriceCalculator { private static final int BUS = 1 ; private static final int SUBWAY = 2 ; private int busPrice (int km) { int extraTotal = km - 10 ; int extraFactor = extraTotal / 5 ; int fraction = extraTotal % 5 ; int price = 5 + extraFactor * 2 ; return fraction > 1 ? price + 2 : price; } private int subwayPrice (int km) { if (km < 6 ) { return 3 ; } else if (km >= 6 && km < 12 ) { return 4 ; } else if (km >= 12 && km < 22 ) { return 5 ; } else if (km >= 22 && km < 32 ) { return 6 ; } else { return 7 ; } } int calculatePrice (int km, int type) { if (type == BUS) { return busPrice(km); } else if (type == SUBWAY) { return subwayPrice(km); } return 0 ; } }
代码写完了,也能用,但是,如果现在添加一个 TAXI 类型,又得在 PriceCalculator 类中修改、添加很多代码,很明显这里并没有遵循单一职责,而且使用了 if - else 判断使用哪种计算方式,再添加时,只能再往后添加 if - else,造成代码的臃肿。
解决办法如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 public interface CalculateStrategy { int calculatePrice (int km) ; } public class BusStrategy implements CalculateStrategy { @Override public int calculatePrice (int km) { int extraTotal = km - 10 ; int extraFactor = extraTotal / 5 ; int fraction = extraTotal % 5 ; int price = 5 + extraFactor * 2 ; return fraction > 1 ? price + 2 : price; } } public class AlgRange { private long start; private long end; private int alg; public AlgRange (long start, long end, int alg) { this .start = start; this .end = end; this .alg = alg; } public int getAlg () { return alg; } public boolean inRange (long size) { return size >= start && size < end; } } public class SubWayStrategy implements CalculateStrategy { private static final List<AlgRange> algs = new ArrayList<>(); static { algs.add(new AlgRange(0 , 6 , 3 )); algs.add(new AlgRange(6 , 12 , 4 )); algs.add(new AlgRange(12 , 22 , 5 )); algs.add(new AlgRange(22 , 32 , 6 )); algs.add(new AlgRange(32 , Long.MAX_VALUE, 7 )); } @Override public int calculatePrice (int km) { int price = 0 ; for (AlgRange algRange : algs) { if (algRange.inRange(km)) { price = algRange.getAlg(); break ; } } return price; } }
这里只贴出了抽象和实现类,由于 SubWayStrategy 实现时,使用了大量的 if - else, 所以这里也可以使用策略模式整理代码,具体请看代码。 最后看看操作策略的类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public class PriceCalculator { private static final int BUS = 1 ; private static final int SUBWAY = 2 ; private static final Map<Integer, CalculateStrategy> algs = new HashMap<>(); static { algs.put(BUS, new BusStrategy()); algs.put(SUBWAY, new SubWayStrategy()); } int calculatePrice (int km, int type) { return algs.get(type).calculatePrice(km); } }
这样就解决掉了所有 if - else ,并且在添加新的策略的时候不会影响到之前的逻辑代码。
Android 中的策略模式 我们对动画设置不同的插值器(TimeInterpolator ) ,可以实现不同的动态效果
Animation 就类似于上面的操作策略的类,之前都是提前初始化好实现类,但是在 Interpolator 是需要设置的
1 2 3 public void setInterpolator (Interpolator i) { mInterpolator = i; }
在使用的时候会根据设置的实现类调用相应的方法。
1 2 3 4 5 public boolean getTransformation (long currentTime, Transformation outTransformation) { final float interpolatedTime = mInterpolator.getInterpolation(normalizedTime); }
这里简单看一下插值器结构图。
感谢
设计模式之美
《Android 源码设计模式解析与实战》
以及上文中的链接