• Android设计模式之策略模式


    今天介绍下策略模式,直接先上UML图

    enter image description here

    策略模式的概念

    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图
    enter image description here

    总结

    优点

    1、 提供了一种替代继承的方法,而且既保持了继承的优点(代码重用),还比继承更灵活(算法独立,可以任意扩展);
    2、 避免程序中使用多重条件转移语句,使系统更灵活,并易于扩展;
    3、 遵守大部分GRASP原则和常用设计原则,高内聚、低偶合;
    4、 易于进行单元测试,各个算法区分开,可以针对每个算法进行单元测试;

    缺点

    1、 因为每个具体策略类都会产生一个新类,所以会增加系统需要维护的类的数量;
    2、 选择何种算法需要客户端来创建对象,增加了耦合,这里可以通过与工厂模式结合解决该问题;
    3、 程序复杂化。

  • 相关阅读:
    允许 使用接口传递对象,为什么?
    一道猫和老鼠吵醒主人的笔试题(C#)
    随心所欲操作Enum枚举类型
    SmartPhone 2003 手机编程实战之一:简单上手 2005年01月08日
    SmartPhone 2003 手机编程实战之二:自己开发一个天气预报服务 2005116
    QQ是危险的、MSN是危险的,所有即时通讯都是危险的
    PWN通用技巧
    Jarvis Oj Pwn 学习笔记level2
    Jarvis Oj Pwn 学习笔记Tell Me Something
    Jarvis Oj Pwn 学习笔记level1
  • 原文地址:https://www.cnblogs.com/android-blogs/p/5706532.html
Copyright © 2020-2023  润新知