策略模式史上最佳实践,没有之一!!!

简介: 策略模式史上最佳实践,没有之一!!!

一、背景


在软件开发中常常遇到这种情况,实现某一个功能有多种算法或者策略,我们可以根据环境或者条件的不同选择不同的算法或者策略来完成该功能。策略模式(Strategy Pattern)定义了一组策略,分别在不同类中封装起来,每种策略都可以根据当前场景相互替换,从而使策略的变化可以独立于操作者。比如我们去某个地方,会根据目标地的距离或者手头经济情况,来选择不同的出行方式(共享单车、公交、滴滴、高铁、飞机等),这些出行方式即是不同的策略。


二、使用时机

了解完背景我们对策略模式心里有个底了,核心是把不同的策略封装起来,在不同的场景把该策略拎出来。额,这咋一看不就是if...else...的逻辑么。既然这样,我那我们就来看下我们最初学代码的那个场景。

if (conditionA) {
    logicA
} else if (conditionB) {
    logicB
} else if (conditionC) {
    logicC
} else {
    logicD
}

当你开始学的时候,问题不大,能跑就行。当你有了一定经验以后,这段代码明显违反了OOP的两个基本原则:

  • 单一职责原则(SPR,Single Responsibility Principle):一个类或者模块只负责完成一个职责或者功能。
  • 开闭原则(OCP,Open Closed Principle):软件实体(模块、类、方法等)应该”对扩展开放、对修改关闭“。


因为违反了这两个原则,当if-else块中的代码量比较大时,后续的代码会变得越来越难以维护,而且不小心就改出问题,到时候等着背锅吧。当然你会问,老周,那我咋样才能避免写这样的代码啊!额,根据老周的经验,当你就两层左右,可以这样写,不要做过度设计;三层以上,但每层代码行比较少的话,可以使用卫语句;当三层以上并且每层代码量比较多时,则需要使用策略模式了。


三、最佳实践


1、需求

比如我们模型训练平台,之前只是模型输入的提交,后面业务方还希望支持在线预览表单提交以及绑定dubbo服务提交给其它平台。

这样的话咱们的表单提交就要响应业务方支持下面三种提交类型:

  • 输入的提交
  • 在线预览表单提交
  • 绑定dubbo服务提交


铁子们,现在我们就来最佳实践一波~


2、定义策略接口

  • 获取策略类型
  • 处理策略逻辑
/**
 * 表单提交处理器
 * @param <R> 业务值
 */
public interface FormSubmitHandler<R extends Serializable> {
    /**
     * 获得提交类型
     * @return 提交类型
     */
    String getSubmitType();
    /**
     * 处理表单提交请求
     * @param request 请求
     * @return 响应,left:为返回给前端的提示信息,right:为业务值
     */
    CommonPairResponse<String, R> handleSubmit(FormSubmitRequest request);
}
/**
 * 表单提交的请求
 */
@Getter
@Setter
public class FormSubmitRequest {
    /**
     * 提交类型
     */
    private String submitType;
    /**
     * 用户id
     */
    private Long userId;
    /**
     * 表单提交的数据
     */
    private Map<String, Object> formInput;
}

其中,FormSubmitHandler 的 getSubmitType 方法用来获取表单的提交类型(即策略类型),用于根据客户端传递的参数直接获取到对应的策略实现;客户端传递的相关参数都被封装为 FormSubmitRequest,传递给 handleSubmit 进行处理。


3、相关策略实现


输入的提交

@Slf4j
@Component
public class ModelSubmitHandler implements FormSubmitHandler<Serializable> {
    public String getSubmitType() {
        return "model";
    }
    public CommonPairResponse<String, Serializable> handleSubmit(FormSubmitRequest request) {
        log.info("模型提交:userId={}, formInput={}", request.getUserId(), request.getFormInput());
        // 模型创建成功后获得模型的 id
        Long modelId = createModel(request);
        return CommonPairResponse.success("模型提交成功!", modelId);
    }
    private Long createModel(FormSubmitRequest request) { // 创建模型的逻辑
        return 123L;
    }
}

在线预览表单提交

@Slf4j
@Component
public class OnlinePreviewSubmitHandler implements FormSubmitHandler<Serializable> {
    public String getSubmitType() {
        return "online preview";
    }
    public CommonPairResponse<String, Serializable> handleSubmit(FormSubmitRequest request) {
        log.info("在线预览提交:userId={}, formInput={}", request.getUserId(), request.getFormInput());
        return CommonPairResponse.success("在线预览模式提交数据成功!", null);
    }
}

绑定dubbo服务提交

@Slf4j
@Component
public class DubboSubmitHandler implements FormSubmitHandler<Serializable> {
    public String getSubmitType() {
        return "dubbo";
    }
    public CommonPairResponse<String, Serializable> handleSubmit(FormSubmitRequest request) {
        log.info("dubbo模式提交:userId={}, formInput={}", request.getUserId(), request.getFormInput());
        // 进行dubbo调用,获得业务方返回的提示信息和业务数据
        CommonPairResponse<String, Serializable> response = dubboSubmitData(request);
        return response;
    }
}

4、建立策略的简单工厂

@Component
public class FormSubmitHandlerFactory implements InitializingBean, ApplicationContextAware {
    private static final Map<String, FormSubmitHandler<Serializable>> FORM_SUBMIT_HANDLER_MAP = new HashMap<>();
    private ApplicationContext applicationContext;
    /**
     * 根据提交类型获取对应的处理器
     * @param submitType 提交类型
     * @return 提交类型对应的处理器
     */
    public FormSubmitHandler<Serializable> getHandler(String submitType) {
        return FORM_SUBMIT_HANDLER_MAP.get(submitType);
    }
    public void afterPropertiesSet() throws Exception {
        // 将 Spring 容器中所有的 FormSubmitHandler 注册到 FORM_SUBMIT_HANDLER_MAP
        applicationContext.getBeansOfType(FormSubmitHandler.class).values().forEach(
                handler -> FORM_SUBMIT_HANDLER_MAP.put(handler.getSubmitType(), handler)
        );
    }
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
}

我们让 FormSubmitHandlerFactory 实现 InitializingBean 接口,在 afterPropertiesSet 方法中,基于 Spring 容器将所有 FormSubmitHandler 自动注册到 FORM_SUBMIT_HANDLER_MAP,从而 Spring 容器启动完成后, getHandler 方法可以直接通过 submitType 来获取对应的表单提交处理器。


Factory 只负责获取 Handler,Handler 只负责处理具体的提交,Service 只负责逻辑编排, 从而达到功能上的 “低耦合高内聚”。

有木有感觉到,整个流程如丝滑般流畅~


5、假设扩展


如果业务方又需要加入一种提交策略,比如新增钩子函数提交。这对我们来说就相当easy了,我们只需要添加新的策略实现即可。

@Slf4j
@Component
public class HookSubmitHandler implements FormSubmitHandler<Serializable> {
    public String getSubmitType() {
        return "hook";
    }
    public CommonPairResponse<String, Serializable> handleSubmit(FormSubmitRequest request) {
        log.info("hook钩子函数提交:userId={}, formInput={}", request.getUserId(), request.getFormInput());
        // 进行 hook 函数调用,并获得业务方返回的提示信息和业务数据
        CommonPairResponse<String, Serializable> response = hookSubmitData(request);
        return response;
    }
}

此时不需要修改任何代码,因为Spring容器重启时会自动将HookSubmitHandler 注册到 FormSubmitHandlerFactory 中,后续业务方又要加其它策略的话,直接加策略实现,也不需要改动之前的代码,“低耦合高内聚”简直不要香。


欢迎小伙伴们关注我的公众号,Java后端主流技术栈的原理、源码分析、架构以及各种互联网高并发、高性能、高可用的解决方案。


喜欢的话,点赞、再看、分享三连。

相关文章
|
5月前
|
设计模式 Java 开发者
设计模式揭秘:Java世界的七大奇迹
【4月更文挑战第7天】探索Java设计模式:单例、工厂方法、抽象工厂、建造者、原型、适配器和观察者,助你构建健壮、灵活的软件系统。了解这些模式如何提升代码复用、可维护性,以及在特定场景下的应用,如资源管理、接口兼容和事件监听。掌握设计模式,但也需根据实际情况权衡,打造高效、优雅的软件解决方案。
38 0
|
设计模式 关系型数据库
设计模式八大原则知多少
设计模式是一种通用的解决问题的经验,可以帮助我们设计出可重用、可维护和可扩展的软件。
|
7天前
|
设计模式 算法 PHP
PHP中的设计模式:策略模式的深入探索与实践在软件开发的广袤天地中,PHP以其独特的魅力和强大的功能,成为无数开发者手中的得力工具。而在这条充满挑战与机遇的征途上,设计模式犹如一盏明灯,指引着我们穿越代码的迷雾,编写出更加高效、灵活且易于维护的程序。今天,就让我们聚焦于设计模式中的璀璨明珠——策略模式,深入探讨其在PHP中的实现方法及其实际应用价值。
策略模式,这一设计模式的核心在于它为软件设计带来了一种全新的视角和方法。它允许我们在运行时根据不同情况选择最适合的解决方案,从而极大地提高了程序的灵活性和可扩展性。在PHP这门广泛应用的编程语言中,策略模式同样大放异彩,为开发者们提供了丰富的创作空间。本文将从策略模式的基本概念入手,逐步深入到PHP中的实现细节,并通过一个具体的实例来展示其在实际项目中的应用效果。我们还将探讨策略模式的优势以及在实际应用中可能遇到的挑战和解决方案,为PHP开发者提供一份宝贵的参考。
|
5月前
|
设计模式 Java 开发者
设计模式:软件开发的秘密武器
设计模式:软件开发的秘密武器
|
2月前
|
设计模式
设计模式问题之业务不稳定的初期和成熟阶段的设计模式如何选择
设计模式问题之业务不稳定的初期和成熟阶段的设计模式如何选择
|
4月前
|
设计模式 C#
技术经验分享:C#设计模式
技术经验分享:C#设计模式
22 0
|
5月前
|
设计模式 算法
【设计模式】阿里终面:你觉得这个例子是策略模式吗?
【设计模式】阿里终面:你觉得这个例子是策略模式吗?
49 1
|
12月前
|
设计模式 自然语言处理 算法
一篇文章读懂六大设计模式
一篇文章读懂六大设计模式
96 1
|
12月前
|
设计模式 算法 Java
JAVA设计模式第十二讲:大厂实践 - 美团: 设计模式二三事
JAVA设计模式第十二讲:大厂实践 - 美团: 设计模式二三事
Zp
初尝策略模式~真香
初尝策略模式~真香
Zp
135 0
下一篇
无影云桌面