设计模式之策略模式

定义

  策略模式的用意是针对一组算法或逻辑,将每一个算法或逻辑封装到具有共同接口的独立的类中,从而使得它们之间可以相互替换。此模式让算法的变化,不会影响到使用算法的客户。算法本身是一种策略,而且这种策略是随时都可能相互替换的,这就是变化点,而封装变化点是面向对象的一种很重要的思维方式。策略模式是一种定义一系列算法的方式,从概念上看,所有这些算法完成的都是相同的工作,只是实现不同,它可以以相同的方式调用所有的算法,减少了各种算法类与使用算法类之间的耦合。

  策略模式体现了这样两个原则——封装变化和对接口编程而不是对实现编程。

使用场景

  • 多个类只区别在表现行为不同,可以使用Strategy模式,在运行时动态选择具体要执行的行为。
  • 需要在不同情况下使用不同的策略(算法),或者策略还可能在未来用其它方式来实现。
  • 对客户隐藏具体策略(算法)的实现细节,彼此完全独立。

  这种例子其实在我们的开发工作中也比较常见,比如支付方式:支付宝,微信,信用卡,储蓄卡等不同的支付方式支付逻辑肯定是不一样的;再比如拥有不同用户类型的网站:会员,超级会员和vip会员的购买价格或者其他的享受的待遇是不一样的;再如,很多框架中的负载均衡策略的实现,这些都是最佳的使用策略模式的地方。

角色构成

  • 环境(Context)角色:持有一个公共策略接口的引用,直接给客户端调用。
  • 抽象策略(Strategy)角色:这是一个抽象角色,通常由一个接口或抽象类实现。此角色给出所有的具体策略类所需的接口。
  • 具体策略(ConcreteStrategy)角色:包装了相关的算法或行为。

优点

  • 策略模式的Strategy类层次为Context定义了一些列的可供重用的算法或行为。继承有助于析取出这些算法的公共功能。
  • 简化了单元测试,因为每个算法都有自己的类,可以通过自己的接口单独测试。
  • 当不同的行为堆砌在一个类中时,很难避免使用条件语句来选择合适的行为。比如你需要在客户端代码中来进行选择使用哪一种算法。将这些行为封装在一个独立的Strategy类中,可以在使用这些行为的类中消除条件语句。策略模式就是用来封装算法的,但在实践中,我们发现可以用它来分装几乎任何类型的规则,只要在分析过程中听到需要在不同实践应用不同的业务规则,就可以考虑使用策略模式处理这种变化的可能性。

缺点

  • 在基本的策略模式中,选择所用具体实现的职责由客户端对象承担,并转给策略模式的Context对象,这本身并没有解除客户端需要选择判断的压力(也就是说客户端需要知道所有的策略类型)。但是可以通过工厂模式进行一定程度的改进,这里需要注意的是如何将区分不同策略的标识传进来。
  • 但策略类增加时,将可能产生大量的子类,如果一个算法被多次使用,但仅是外部条件不同的时候,可以考虑使用享元模式来进行优化,减少实例的个数,但这在涉及线程安全的时候需要格外注意。

与工厂模式的区别

  工厂模式是创建型模式 ,它关注对象创建,提供创建对象的接口,让对象的创建与具体的使用客户无关。 策略模式是对象行为型模式 ,它关注行为和算法的封装 。再举个例子,还是我们出去旅游,对于策略模式我们只需要选择其中一种出行方法就好,但是工厂模式不同,工厂模式是你决定哪种旅行方案后,由工厂代替你去构建具体方案(工厂代替你去买火车票)。

与状态模式的区别

  策略模式只是条件选择方法,只执行一次方法,而状态模式是随着状态的改变不停地更改执行方法。举个例子,就好比我们旅游,对于策略模式我们只需要选择其中一种出行方法就好了,但是状态模式不一样,可能我们到了A地点选择的是火车,到了B地点又选择飞机,根据不同的状态选择不同的出行方式。

代码

抽象策略类

public interface Strategy {
    /**
     * 策略方法 
    */  
    void algorithmInterface();
}

具体策略类

public class ConcreteStrategyA implements Strategy {
    @Override
    public void algorithmInterface() {
        System.out.println("我是ConcreteStrategyA...");
    }
}
public class ConcreteStrategyB implements Strategy {
    @Override
    public void algorithmInterface() {
        System.out.println("我是ConcreteStrategyB...");
    }
}

抽象环境角色

public abstract class Context {
    // 策略接口
    protected Strategy strategy;

    /**
     * 设置具体策略角色,动态指定具体策略角色 
     * @param strategy 具体策略角色
     */
    public void setStrategy(Strategy strategy) {
        this.strategy = strategy;
    }

    /**
     * 策略方法 
     */
    public abstract void contextInterface();
}

具体环境角色

public class ContextA extends Context {

    @Override
    public void contextInterface() {
        strategy.algorithmInterface();
    }
}

  之所以要抽象环境角色和具体环境角色之分,是因为在某些场景下我们需要在策略方法执行之前或之后执行其他的业务逻辑,同时某些情况下又不需要这些额外的业务逻辑,这是我们可以不同的场景创建不同的环境角色来达到我们的目的。

下一个模式

原文地址:https://www.cnblogs.com/htyj/p/12844746.html