掘金 后端 ( ) • 2024-04-14 11:04

策略模式

  • 策略模式将各种不同类型所对应的不同执行逻辑抽象成算法族(策略组),并分别封装起来,让他们之间可以互相替换,这样就将算法的变化和使用算法的客户端分离开来。
  • 这样体现了几个设计原则:
    • 把变化的代码从不变的代码中分离出来。
    • 针对接口编程而不是具体类(定义了策略接口)。
    • 多用组合/聚合,少用继承(客户通过组合方式使用策略)。

进一步阐述:策略模式就是解决具体逻辑和客户端耦合在一起的问题,其核心是找到项目中变化的部分并进行抽离出来成为一个接口,项目中不变的部分就直接使用接口调用抽象方法即可。

策略模式包含三种角色:

  • Context(环境角色):用来操作策略的上下文环境,它持有一个Strategy的引用。
  • Strategy(抽象策略角色):所有策略的抽象,封装了需要去执行的项目中不变的部分逻辑。
  • ConcreteStrategy (具体策略角色):抽象策略的实现,每一种不同的逻辑执行方式就是一个具体的策略类。

案例:我们以一个旅行者为例,旅行的方式有很多种,可以坐高铁,也可以坐飞机,这取决于用户自己的选择。其中旅行者Traveler类就是环境角色,旅行策略TravelingStrategy接口就是抽象策略角色,飞机旅行策略AirTravelingStrategy实现类和高铁旅行策略HightTrainTravelingStrategy实现类就是具体的策略角色。

UML类图:

策略模式.jpg

客户端Client类:

/**
 * 客户端
 */
public class Client {
​
    public static void main(String[] args) {
​
        Traveler traveler = new Traveler(new HightTrainTravelingStrategy());
​
        traveler.goTraveling();
    }
}

旅行者Traveler类:

/**
 * 旅行者类
 */
public class Traveler {
​
    //持有一个旅行策略接口的实现类
    private TravelingStrategy travelingStrategy;
​
    public Traveler(TravelingStrategy travelingStrategy) {
        this.travelingStrategy = travelingStrategy;
    }
​
    public void goTraveling() {
        //省去了繁琐的if-- else if -- else if
        travelingStrategy.goTraveling();
    }
}

旅行策略TravelingStrategy接口:

/**
 * 旅行策略接口
 */
public interface TravelingStrategy {
​
    /**
     * 去旅游方法
     */
    void goTraveling();
}

高铁旅行策略HightTrainTravelingStrategy实现类:

/**
 * 高铁旅行策略实现类
 */
public class HightTrainTravelingStrategy implements TravelingStrategy {
​
    @Override
    public void goTraveling() {
        System.out.println("乘坐高铁去旅行……");
    }
}

飞机旅行策略AirTravelingStrategy实现类:

/**
 * 飞机旅行策略实现类
 */
public class AirTravelingStrategy implements TravelingStrategy {
​
    @Override
    public void goTraveling() {
        System.out.println("乘坐飞机去旅行……");
    }
}

总结:

  • 客户端增加行为不用修改原有代码,只要添加一种策略(或者行为)即可,避免了使用if--else if--else if去完成逻辑的编程的情况,是典型的if--else if--else if的“杀手”,因为遵守了开闭原则(OCP原则),所以极大地提高了软件的可扩展性、可维护性。
  • 策略模式的核心思想是:多用组合或者聚合少用继承;用行为类进行组合,而不是行为类进行继承,这样耦合度降低了,这样就替换了用继承关系解决问题的办法,策略模式将算法封装在独立的 Strategy 类中使得你可以独立于其 Context 改变它,使它易于切换、易于理解、易于扩展遵守了合成复用原则。
  • 使用策略模式的客户端必须知道所有的策略类,并自行决定使用哪一个策略类。
  • 策略模式如果策略有很多那么就会出现很多的策略类增加了系统类的复杂度。
  • Context在使用这些策略类的时候,这些策略类由于实现了策略接口,所以有些数据可能用不到,但是依然初始化了,这样就造成了内存的浪费。

典型运用场景举例: 电商项目的优惠活动管理,优惠的策略有很多种,包括满减类型优惠策略、打折类优惠策略、赠送礼品策略、发放优惠券策略。