策略模式极简教程

简介: 策略模式(Strategy Pattern)是一种软件设计模式,属于行为型设计模式。一般的,如果程序中存在对同一个场景做不同的业务处理实现的时候可以考虑使用策略模式。使用策略模式编排代码可以解决 if-else 带来的代码复杂度高、易读性差,难以维护的问题。

简介

策略模式(Strategy Pattern)是一种软件设计模式,属于行为型设计模式。

一般的,如果程序中存在对同一个场景做不同的业务处理实现的时候可以考虑使用策略模式。

使用策略模式编排代码可以解决 if-else 带来的代码复杂度高、易读性差,难以维护的问题。

优点:

1、业务实现算法可以自由切换。

2、避免使用多重条件判断。

3、扩展性良好。

缺点:

1、代码量增多。

2、所有策略类都需要对外暴露。

3、当策略过多的时候会引发策略膨胀的问题。

类图

策略模式类图

业务场景以加减乘运算为背景:

  • 定义了一个业务处理接口 Strategy ,其中定义了一个 doOperation 的方法,此方法接收 int 类型的参数。
  • 编写了三个业务处理实现类,分别重写 doOperation 方法来实施具体的运算逻辑。
  • 定义一个策略上下文类 Context ,存放所有的策略,并定义一个executeStrategy 方法用来执行某一个策略。
  • 在程序 main 方法中调用 策略上下文类实现程序功能。
  • 具体的业务处理实现类可以根据需要删除、增加,在 策略上下文中维护可用的策略有哪些

举例

以SpringBoot项目举例如何在代码中编写策略模式

业务描述

业务背景为一个视频监控系统,该系统可实现对摄像头的实时预览、云台操作等功能。该系统可以集成多个厂家的摄像头,如海康、大华、天地伟业等。

业务流程为:

  • 摄像头列表页面展示所有摄像头
  • 点击列表页的 播放 按钮,查询出该摄像头的实时预览 rtsp-url
  • 需要根据摄像头唯一标识 cameraId 查询出该摄像头的类型,再根据不同的类型调用不同的业务实现类获取预览的 rtsp-url

代码实现

定义操作摄像机接口


public interface CameraActionStrategy {
    String getRtspUrl(String cameraId);
}
  • 该接口有一个获取rtspUrl 的方法,入参为摄像机唯一标识

编写操作摄像机实现


@Slf4j
@Service
public class HikvisionCameraService  implements CameraActionStrategy {
    @Override
    public String getRtspUrl(String cameraId){
        //参数校验
        //调用海康 openApi 获取视频预览url
        //异常处理和资源释放
        return "rtsp://hikvision/1/1";
    }
}

@Slf4j
@Service
public class DahuaCameraService  implements CameraActionStrategy {
    @Override
    public String getRtspUrl(String cameraId){
        //参数校验
        //调用大华 openApi 获取视频预览url
        //异常处理和资源释放
        return "rtsp://dahua/1/1";
    }
}

@Slf4j
@Service
public class TiandyCameraService  implements CameraActionStrategy {
    @Override
    public String getRtspUrl(String cameraId){
        //参数校验
        //调用天地伟业 openApi 获取视频预览url
        //异常处理和资源释放
        return "rtsp://tiandy/1/1";
    }
}
  • 每一个实现类均实现 getRtspUrl 方法,并编写自己特有的获取预览视频的代码逻辑,例如这里可能要根据各个设备厂商的集成规范对接 openApi 平台,甚至可能是根据SDK直连设备。
  • 返回一个可用的 rtsp-url 播放地址
  • 这种业务场景涉及对接,需要考虑超时如何处理

编写策略上下文


@AllArgsConstructor
@Component
public class CameraActionStrategyContext{
    private CameraMapper cameraMapper;
    private final Map<String, CameraActionStrategy> strategyMap = new ConcurrentHashMap<>();
    public CameraActionStrategy getPreviewStrategy(String cameraId) {
        if (StringUtils.isEmpty(cameraId)) {
            return null;
        }
        //获取相机类型
        Camera camera = cameraMapper.getCameraDetail(cameraId);
        //数据校验
        String strategyServiceName = CameraStrategyTypeEnum.getServiceName(camera.getType);
        return strategyMap.get(strategyServiceName);
    }
}

@Getter
@AllArgsConstructor
enum CameraStrategyTypeEnum {
    HIKVISION("hikvision", "hikvisionCameraService", "海康"),
    DAHUA("dahua", "dahuaCameraService", "大华"),
    TIANDY("tiandy", "tiandyCameraService", "天地伟业"),
    ;
    private final String cameraType;
    private final String strategyServiceName;
    private final String describe;
    /**
     * 获取策略实现service - 根据 cameraType
     *
     * @param cameraType
     * @return
     */
    public static String getServiceName(String cameraType) {
        if (StringUtils.isEmpty(cameraType)) {
            return null;
        }
        String v = null;
        for (CameraStrategyTypeEnum i : CameraStrategyTypeEnum.values()) {
            if (i.getCameraId() != null && i.getCameraId().equals(cameraType)) {
                v = i.getStrategy();
                break;
            }
        }
        return v;
    }
}
  • 策略上下文类需要注入到容器中,这样 bizService 可以直接注入并调用上下文从而在运行时获取不同的策略
  • 注入了一个 CameraMapper ,用来根据 id 获取 type
  • 因为添加了 @AllArgsConstructor 注解,所以当此类实例化的时候,会通过构造函数注入的方式获取 CameraActionStrategy 接口的所有实现类,并存放到局部变量 strategyMap 中,key为实现类的名字,若不加别名默认就是类名全拼(首字母小写)。value为策略实现类实例,有了类实例就可以直接访问类中的方法了
  • 编写一个方法获取预览视频策略实现类,如果有多种不同业务场景可定义不同的获取XX策略实现类的方法
  • 上下文类中添加一个内部枚举,维护CameraType 和策略实现类名称之间的关系,这样就可以根据 cameraId 找到策略实现类了,请求发起方无需关心摄像头是什么厂商的,只管发起请求就可以返回预览url了

编写播放按钮处理逻辑


@Slf4j
@AllArgsConstructor
@Service
public class CameraService {
    private CameraActionStrategyContext  cameraActionStrategyContext;
    public String getPreviewUrl(String cameraId){
        //参数校验
        //数据校验: camera 是否存在,是否可用等

        CameraActionStrategy strategy = cameraActionStrategyContext.getPreviewStrategy(cameraId);    
        return strategy.getRtspUrl(cameraId);
    }
}

引用

https://www.runoob.com/design-pattern/strategy-pattern.html

目录
相关文章
|
设计模式 算法 Java
设计模式系列教程(08) - 模板方法
设计模式系列教程(08) - 模板方法
48 0
|
7月前
|
设计模式 Java
细说一下设计模式中的策略模式!
细说一下设计模式中的策略模式!
57 0
|
设计模式 算法 关系型数据库
设计模式系列教程(13) - 策略模式
设计模式系列教程(13) - 策略模式
39 0
|
7月前
|
设计模式 算法 Java
【设计模式系列笔记】策略模式
策略模式(Strategy Pattern)是一种行为设计模式,它定义了一系列的算法,将每个算法封装起来,并且使它们可以互相替换。策略模式使得算法可以独立于客户端而变化。
116 0
|
7月前
|
设计模式 SQL 算法
【设计模式系列笔记】模板方法模式
模板方法模式是一种行为设计模式,它定义了一个算法的骨架,并允许子类在不改变该算法结构的情况下重新定义算法的某些步骤。这种模式属于行为型模式,它通过将算法的不同部分封装在不同的方法中,从而使子类能够在不改变算法结构的前提下定制算法的某些步骤。
66 0
|
7月前
|
设计模式 Java 数据安全/隐私保护
Java设计模式【二十三】:策略模式
Java设计模式【二十三】:策略模式
66 0
|
7月前
|
设计模式 算法
设计模式--策略模式(由简单工厂到策略模式到两者结合图文详解+总结提升)
设计模式--策略模式(由简单工厂到策略模式到两者结合图文详解+总结提升)
|
设计模式 Java
设计模式学习笔记(2)——策略模式
设计模式学习笔记(2)——策略模式
55 0
|
设计模式 算法
【设计模式】 | 策略模式源码学习与实践
在业务开发中,我们最经常使用到的判断就是if...else,只要涉及到多种策略的实现方式,我们脑海中就会使用这个判断。
|
设计模式 算法
二十三种设计模式之策略模式
二十三种设计模式之策略模式
149 0