设计模式之策略模式

357 阅读4分钟

「这是我参与11月更文挑战的第12天,活动详情查看:2021最后一次更文挑战

1. 概述

策略模式定义了算法族(一组行为),分别封装起来,让他们之间可以互相替换,此模式让算法的变化独立于使用算法的客户。

2. 结构

策略模式UML

image.png

策略模式包含如下角色:

  • 环境类(Context):持有一个Strategy类的引用
  • 抽象策略类(Strategy):一个抽象角色,由一个接口或抽象类实现。提供所有的具体策略类所需的接口。
  • 具体策略类(ConcreteStrategy):包装了相关的算法或行为。
//环境类
public class Context {
  private Strategy strategy;
  /**
  * 策略方法
  **/ 
  public void contextInterface() {
    strategy.strategyInterface();
  }
}

//抽象策略类
abstract public class Strategy {
  /**
  * 策略方法
  **/ 
  public abstract void strategyInterface();
}

//具体策略类
public class ConcreteStrategy extends Strategy {
  /**
  * 策略方法
  **/ 
  public void strategyInterface() {
    //方法具体实现
  }
}

3. 例子

我们的商品卖个不同的客户,不同的销量,不同的价格

  • 新客户小批量售卖
  • 新客户大批量售卖
  • 老客户小批量售卖
  • 老客户大批量售卖 具体采用哪一个报价策略,需要根据实际情况确认 最简单的写法就是if else 如:
if(type.equals("新客户小批量售卖")){
 System.out.println("原价销售");
 return price*1;
}else if(type.equals("新客户大批量售卖")){
 System.out.println("九折销售");
 return price*0.9;
}else if(type.equals("老客户小批量售卖")){
 System.out.println("八折销售");
 return price*0.8;
}else if(type.equals("老客户大批量售卖")){
 System.out.println("八折销售");
 return price*0.7;
}

使用策略模式写代码

3.1 定义一个接口,表示报价

public interface Strategy {
    public double getPrice(double stardardPrice);
}

3.2 定义一种策略

老用户打8折

public class OldCustomerManyStrategy implements Strategy {

    @Override
    public double getPrice(double stardardPrice) {
        System.out.println("打8折");
        return stardardPrice*0.8;
    }
}

新用户打9折扣

public class NewCustomerManyStrategy implements Strategy {

    @Override
    public double getPrice(double stardardPrice) {
        System.out.println("打9折");
        return stardardPrice*0.9;
    }
}

3.3 实现一个context

用来管理报价,客户端直接和context进行交互

public class Context {
    private Strategy strategy;

    public Context(Strategy strategy){
        this.strategy = strategy;
    }
    public Strategy getStrategy() {
        return strategy;
    }

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

    public void pringPrice(double s){
        System.out.println("应该的报价是: "+strategy.getPrice(s));
    }
}

3.4 客户端client调用

客户端选择一种策略,传递给Context,由Context具体执行

public class Client {
    public static void main(String[] args){
        Strategy strategy = new OldCustomerFewStrategy();
        Context context = new Context(strategy);
        context.pringPrice(998);
    }
}

4. 优点&缺点

4.1 优点

  • 策略模式提供了对“开闭原则”的完美支持,用户可以在不修改原有系统的基础上选择算法或行为,也可以灵活地增加新的算法或行为。
  • 策略模式提供了管理相关的算法族的办法。
  • 策略模式提供了可以替换继承关系的办法。
  • 使用策略模式可以避免使用多重条件转移语句。

4.2 缺点

  • 客户端必须知道所有的策略类,并自行决定使用哪一个策略类。 
  • 策略模式将造成产生很多策略类。

4.3 适用环境

  • 如果在一个系统里面有许多类,它们之间的区别仅在于它们的行为,那么使用策略模式可以动态地让一个对象在许多行为中选择一种行为。
  • 一个系统需要动态地在几种算法中选择一种。
  • 如果一个对象有很多的行为,如果不用恰当的模式,这些行为就只好使用多重的条件选择语句来实现。
  • 不希望客户端知道复杂的、与算法相关的数据结构,在具体策略类中封装算法和相关的数据结构,提高算法的保密性与安全性。

5. 策略模式的应用

线程池ThreadPoolExecutor的四种拒绝策略,以及我们可以自定义线程池拒绝策略。

  • AbortPolicy 直接抛出异常
  • CallerRunsPolicy 只调用调用者所在线程
  • DIscardOldestPolicy 丢弃队列中的最近的一个任务
  • DIscardPolicy 不处理,丢弃掉

image.png

总结

策略模式是一个比较容易理解和使用的设计模式,策略模式是对算法的封装,它把算法的责任和算法本身分割开,委派给不同的对象管理。策略模式通常把一个系列的算法封装到一系列的策略类里面,作为一个抽象策略类的子类。用一句话来说,就是“准备一组算法,并将每一个算法封装起来,使得它们可以互换”。

在策略模式中,应当由客户端自己决定在什么情况下使用什么具体策略角色。
策略模式仅仅封装算法,提供新算法插入到已有系统中,以及老算法从系统中“退休”的方便,策略模式并不决定在何时使用何种算法,算法的选择由客户端来决定。这在一定程度上提高了系统的灵活性,但是客户端需要理解所有具体策略类之间的区别,以便选择合适的算法,这也是策略模式的缺点之一,在一定程度上增加了客户端的使用难度。

本文已参与「新人创作礼」活动,一起开启掘金创作之路。