设计模式(十一)---策略模式

 这篇博客我们来介绍一下策略模式(Strategy Pattern,或者叫 Policy Pattern),也是行为型模式之一。通常在软件开发中,我们为了一个功能可能会设计多种算法和策略,然后根据实际使用情况动态选择对应的算法和策略,比如排序算法中的快速排序,冒泡排序等等,根据时间和空间的综合考虑进行运行时选择。 
  针对这种情况,一个常规的方法是将多种算法写在一个类中,每一个方法对应一个具体的排序算法,或者写在一个方法中,通过 if…else 或者 switch…case 条件来选择具体的排序算法。这两种方法我们都成为硬编码,虽然很简单,但是随着算法数量的增加,这个类就会变得越来越臃肿,维护的成本就会变高,修改一处容易导致其他地方的错误,增加一种算法就需要修改封装算法类的源代码,即违背了开闭原则单一职责原则。 

  如果将这些算法或者策略抽象出来,提供一个统一的接口,不同的算法或者策略有不同的实现类,这样在程序客户端就可以通过注入不同的实现对象来实现算法或者策略的动态替换,这种模式的可扩展性、可维护性也就更高,这就是下面讲到的策略模式。 

特点

  策略模式定义了一系列的算法,并将每一个算法封装起来,而且使他们可以相互替换,让算法独立于使用它的客户而独立变化。 

策略模式的使用场景:

  • 针对同一类型问题的多种处理方式,仅仅是具体行为有差别时;
  • 需要安全地封装多种同一类型的操作时;
  • 出现同一抽象类有多个子类,而又需要使用 if-else 或者 switch-case 来选择具体子类时。

UML类图

我们来看看策略模式的 uml 类图: 

è¿éåå¾çæè¿°

策略模式有三个角色:

  • Context:用来操作策略的上下文环境;
  • Stragety:策略的抽象;
  • ConcreteStrategy:具体的策略实现。

据此我们可以写出策略模式的通用代码: 

Stragety.class

public interface Stragety {
    void algorithm();
}

ConcreteStragetyA.class 和 ConcreteStragetyB.class

public class ConcreteStragetyA implements Stragety{
    @Override
    public void algorithm() {
        System.out.print("ConcreteStragetyA\n");
    }
}
public class ConcreteStragetyB implements Stragety{
    @Override
    public void algorithm() {
        System.out.print("ConcreteStragetyB\n");
    }
}

默认策略类ConcreteStragetyDefault.class

public class ConcreteStragetyDefault implements Stragety{
    @Override
    public void algorithm() {
        System.out.print("null stragety");
    }
}

Context.class 和测试代码

public class Context {

    private Stragety stragety;

    public Context() {
        stragety = new ConcreteStragetyDefault();
    }

    public void algorithm() {
        stragety.algorithm();
    }

    public void setStragety(Stragety stragety) {
        if (stragety == null) {
            throw new IllegalArgumentException("argument must not be null!!!");
        }
        this.stragety = stragety;
    }

    public static void main(String args[]) {
        Context context = new Context();
        context.setStragety(new ConcreteStragetyA());
        context.algorithm();
        context.setStragety(new ConcreteStragetyB());
        context.algorithm();
    }
}

代码很简单,一目了然,没有if-else,没有 switch-case。核心就是建立抽象,将不同的策略构建成一个个具体的策略实现,通过不同的策略实现算法替换,在简化逻辑、结构的同时,增强系统的可读性、稳定性和可扩展性,这对于较为复杂的业务逻辑显得更为直观,扩展也更加方便。

示例与源码

Android 源码中策略模式

其实在 Android 源码中策略模式使用的次数也是很多,大家常见的动画中就有使用到策略模式:

public abstract class Animation implements Cloneable {
    /**
     * The interpolator used by the animation to smooth the movement.
     */
    Interpolator mInterpolator;
    ....
    /**
     * Sets the acceleration curve for this animation. Defaults to a linear
     * interpolation.
     *
     * @param i The interpolator which defines the acceleration curve
     * @attr ref android.R.styleable#Animation_interpolator
     */
    public void setInterpolator(Interpolator i) {
        mInterpolator = i;
    }
    ....
    /**
     * Gets the transformation to apply at a specified point in time. Implementations of this
     * method should always replace the specified Transformation or document they are doing
     * otherwise.
     *
     * @param currentTime Where we are in the animation. This is wall clock time.
     * @param outTransformation A transformation object that is provided by the
     *        caller and will be filled in by the animation.
     * @return True if the animation is still running
     */
    public boolean getTransformation(long currentTime, Transformation outTransformation) {
        ......

            final float interpolatedTime = mInterpolator.getInterpolation(normalizedTime);
            applyTransformation(interpolatedTime, outTransformation);
        ......
    }
}

Animation 类就是很典型用到策略模式的类,它里面会有一个 Interpolator 插值器对象,用来在执行动画的时候达到所需要的速度变化效果,系统提供的插值器有 LinearInterpolator(线性插值器,动画的执行速度相等),AccelerateDecelerateInterpolator (加速减速插值器,动画的执行起始加速,结尾减速),DecelerateInterpolator(减速插值器,速度随着动画的执行变慢),以及回弹插值器等等,感兴趣的上网查阅一下相关资料即可(我曾经在 android下拉刷新框架用过插值器的相关类,是一个很有用的类)。

wiki example

  这里我就仍然以 wiki 上的代码为例,商场在不同时段会有打折促销活动,顾客在不同的时段分别进行购买,最后得出一个价格: 

BillingStragety.class

interface BillingStrategy {
    public double getActPrice(double rawPrice);
}

NormalStrategy.class 和 HappyHourStragety.class

// Normal billing strategy (unchanged price)
class NormalStrategy implements BillingStrategy {
    @Override
    public double getActPrice(double rawPrice) {
        return rawPrice;
    }
}
// Strategy for Happy hour (50% discount)
class HappyHourStrategy implements BillingStrategy {
    @Override
    public double getActPrice(double rawPrice) {
        return rawPrice * 0.5;
    }

}

Customer.class

class Customer {

    private List<Double> drinks;
    private BillingStrategy strategy;

    public Customer(BillingStrategy strategy) {
        this.drinks = new ArrayList<Double>();
        this.strategy = strategy;
    }

    public void add(double price, int quantity) {
        drinks.add(strategy.getActPrice(price * quantity));
    }

    // Payment of bill
    public void printBill() {
        double sum = 0;
        for (Double i : drinks) {
            sum += i;
        }
        System.out.println("Total due: " + sum);
        drinks.clear();
    }

    // Set Strategy
    public void setStrategy(BillingStrategy strategy) {
        this.strategy = strategy;
    }

}

main

public class StrategyPatternWiki {

    public static void main(String[] args) {
        Customer firstCustomer = new Customer(new NormalStrategy());

        // Normal billing
        firstCustomer.add(1.0, 1);

        // Start Happy Hour
        firstCustomer.setStrategy(new HappyHourStrategy());
        firstCustomer.add(1.0, 2);

        // New Customer
        Customer secondCustomer = new Customer(new HappyHourStrategy());
        secondCustomer.add(0.8, 1);
        // The Customer pays
        firstCustomer.printBill();

        // End Happy Hour
        secondCustomer.setStrategy(new NormalStrategy());
        secondCustomer.add(1.3, 2);
        secondCustomer.add(2.5, 1);
        secondCustomer.printBill();

    }
}

不同时段买的东西,最后得出一个价钱,多种算法动态切换,用户不用去关心具体的内部细节。

总结

  策略模式主要是用来分离算法,在相同的行为抽象下有不同的具体实现策略。这个模式很好的演示了开闭原则:定义抽象,增加新的策略只需要增加新的类,然后在运行中动态更换即可,没有影响到原来的逻辑,从而达到了很好的可扩展性。 
优点:

  1. 结构清晰明了、使用简单直观;
  2. 耦合度相对而言较低,扩展方便;
  3. 操作封装也更为彻底,数据更为安全。

  缺点也很明显,这当然也是很多设计模式的通病:类的增多,随着策略的增加,子类也会变得繁多。

源码下载

https://github.com/zhaozepeng/Design-Patterns/tree/master/StrategyPattern

引用

https://en.wikipedia.org/wiki/Strategy_pattern 
http://blog.csdn.net/jason0539/article/details/45007553

发布了205 篇原创文章 · 获赞 217 · 访问量 235万+

猜你喜欢

转载自blog.csdn.net/heng615975867/article/details/105023656