今天介绍下策略模式,直接先上UML图
策略模式的概念
The Strategy Pattern defines a family of algorithms,encapsulates each one,and makes them interchangeable. Strategy lets the algorithm vary independently from clients that use it.
–(翻译)– 定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换。策略模式使得算法可独立于使用它的客户而变化。
1、需要使用ConcreteStrategy提供的算法。
2、 内部维护一个Strategy的实例。
3、 负责动态设置运行时Strategy具体的实现算法。
4、负责跟Strategy之间的交互和数据传递。
策略模式的组成
— Strategy(抽象策略类): 通常由一个接口或者抽象类实现。
— ConcreteStrategy(具体策略角色):包装了相关的算法和行为。一般有多个
— Context(环境角色):持有一个策略类的引用,最终给客户端调用。
策略模式的应用场景
定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换。策略模式使得算法可独立于使用它的客户而变化。实际开发中用途,比如google开源的android网络框架volley,volley的超时重发重发机制就使用到了典型策略模式,看源码
RetryPolicy.java 请求重试策略类,定义了三个接口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
|
package com.android.volley;
/** * 请求重试策略类 * 此类属于抽象策略类对应Strategy */ public interface RetryPolicy {
/** * 超时时间 */ public int getCurrentTimeout();
/** * 重试次数 */ public int getCurrentRetryCount();
/** * 针对错误异常的处理 */ public void retry(VolleyError error) throws VolleyError; }
|
DefaultRetryPolicy.java 默认重试策略,继承了RetryPolicy ,具体实现了接口,我截取了部分代码
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
|
public class DefaultRetryPolicy implements RetryPolicy { ... public static final int DEFAULT_TIMEOUT_MS = 2500; private int mCurrentTimeoutMs; /** The default number of retries */ public static final int DEFAULT_MAX_RETRIES = 0;
/** The default backoff multiplier */ public static final float DEFAULT_BACKOFF_MULT = 1f;
...//代表省略掉的代码
@Override public int getCurrentTimeout() { return mCurrentTimeoutMs; }
@Override public int getCurrentRetryCount() { return mCurrentRetryCount; }
@Override public void retry(VolleyError error) throws VolleyError { mCurrentRetryCount++; mCurrentTimeoutMs += (mCurrentTimeoutMs * mBackoffMultiplier); if (!hasAttemptRemaining()) { throw error; } } }
|
Request.java 请求类,持有一个抽象策略类RetryPolicy 的引用,最终给客户端调用。截取部分代码
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
|
public abstract class Request<T> implements Comparable<Request<T>> { private RetryPolicy mRetryPolicy; //持有策略类的引用
...
public Request(int method, String url, Response.ErrorListener listener) { mMethod = method; mUrl = url; mIdentifier = createIdentifier(method, url); mErrorListener = listener; setRetryPolicy(new DefaultRetryPolicy()); //设置策略类
mDefaultTrafficStatsTag = findDefaultTrafficStatsTag(url); } ...
public Request<?> setRetryPolicy(RetryPolicy retryPolicy) { mRetryPolicy = retryPolicy; return this; }
...
public final int getTimeoutMs() { return mRetryPolicy.getCurrentTimeout(); } }
|
BasicNetwork.java 网络处理类,处理Request,使用到了对应的重试策略,截取部分代码
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
|
/** *遇到异常请求使用到了策略类的getTimeoutMs()方法 */ private static void attemptRetryOnException(String logPrefix, Request<?> request, VolleyError exception) throws VolleyError { RetryPolicy retryPolicy = request.getRetryPolicy(); int oldTimeout = request.getTimeoutMs();
try { retryPolicy.retry(exception); } catch (VolleyError e) { request.addMarker( String.format("%s-timeout-giveup [timeout=%s]", logPrefix, oldTimeout)); throw e; } request.addMarker(String.format("%s-retry [timeout=%s]", logPrefix, oldTimeout)); }
...
/** * 网速慢,获取重试策略的重试次数getCurrentRetryCount */ private void logSlowRequests(long requestLifetime, Request<?> request, byte[] responseContents, StatusLine statusLine) { if (DEBUG || requestLifetime > SLOW_REQUEST_THRESHOLD_MS) { VolleyLog.d("HTTP response for request=<%s> [lifetime=%d], [size=%s], " + "[rc=%d], [retryCount=%s]", request, requestLifetime, responseContents != null ? responseContents.length : "null", statusLine.getStatusCode(), request.getRetryPolicy().getCurrentRetryCount()); } }
...
private static void attemptRetryOnException(String logPrefix, Request<?> request, VolleyError exception) throws VolleyError { RetryPolicy retryPolicy = request.getRetryPolicy(); int oldTimeout = request.getTimeoutMs();
try { retryPolicy.retry(exception); } catch (VolleyError e) { request.addMarker( String.format("%s-timeout-giveup [timeout=%s]", logPrefix, oldTimeout)); throw e; } request.addMarker(String.format("%s-retry [timeout=%s]", logPrefix, oldTimeout)); }
|
实际使用中,我们可以自定义自己的策略继承RetryPolicy,这样就可以按照我们自己请求策略执行。
下面介绍一个比较简单的实例
自定义简单的策略模式实例
商品实现针对不同等级会员显示对应的会员价,比如一本书原价100元,一级会员打97折,三级会员打8折
PriceStrategy.java,策略类,可以使抽象类或接口,定义算法的公共接口,
1 2 3 4 5 6 7 8
|
/** * 定义一个计算价格的接口 * 它属于抽象策略类 * @author su */ public interface PriceStrategy { double priceStrategyInterface(double price); }
|
SuperVipStrategy.java,超级会员算法,打八折,继承PriceStrategy
1 2 3 4 5 6 7 8 9 10 11 12 13
|
/** * 超级会员类 * 它属于具体策略类,继承了计算价格接口 * @author su * */ public class SuperVipStrategy implements PriceStrategy {
@Override public double priceStrategyInterface(double price) { return price * 0.8; //打八折 } }
|
OneVipStrategy.java,一级会员算法,打九七折,继承PriceStrategy
1 2 3 4 5 6 7 8 9 10 11 12
|
/** * 一级会员类 * 它属于具体策略类 * @author su * */ public class OneVipStrategy implements PriceStrategy { @Override public double priceStrategyInterface(double price) { return 0.97 * price; //打九七折 } }
|
ThreeVipStrategy.java,三级会员算法,打九五折,继承PriceStrategy
1 2 3 4 5 6 7 8 9 10 11
|
/** * 三级会员商品类 * 它属于具体策略类 * @author su */ public class ThreeVipStrategy implements PriceStrategy { @Override public double priceStrategyInterface(double price) { return 0.95 * price; //九五折 } }
|
Price.java 上下文环境,持有PriceStrategy 的引用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
|
/** * 环境角色 * @author su * */ public class Price { private PriceStrategy priceStrategy; public Price(PriceStrategy priceStrategy){ this.priceStrategy = priceStrategy; } public double getVipPrice(double price){ return priceStrategy.priceStrategyInterface(price); } }
|
测试实例
1 2 3 4 5 6 7 8 9 10 11
|
public class Test { public static void main(String[] args){ SuperVipStrategy supVipSrategy = new SuperVipStrategy(); OneVipStrategy onVipStrategy = new OneVipStrategy(); System.out.print("超级会员价="+new Price(supVipSrategy).getVipPrice(100)); System.out.print("一级员价="+new Price(onVipStrategy).getVipPrice(100)); } }
-----输出结果--- 超级会员价=80.0一级员价=97.0
|
实例的uml图
总结
优点
1、 提供了一种替代继承的方法,而且既保持了继承的优点(代码重用),还比继承更灵活(算法独立,可以任意扩展);
2、 避免程序中使用多重条件转移语句,使系统更灵活,并易于扩展;
3、 遵守大部分GRASP原则和常用设计原则,高内聚、低偶合;
4、 易于进行单元测试,各个算法区分开,可以针对每个算法进行单元测试;
缺点
1、 因为每个具体策略类都会产生一个新类,所以会增加系统需要维护的类的数量;
2、 选择何种算法需要客户端来创建对象,增加了耦合,这里可以通过与工厂模式结合解决该问题;
3、 程序复杂化。