基于Spring自动注入快速实现策略模式+工厂模式优化过多的if..else

一、策略模式

1.1策略模式定义

在策略模式(Strategy Pattern)中一个类的行为或其算法可以在运行时更改。这种类型的设计模式属于行为型模式。

在策略模式定义了一系列算法或策略,并将每个算法封装在独立的类中,使得它们可以互相替换。通过使用策略模式,可以在运行时根据需要选择不同的算法,而不需要修改客户端代码。

在策略模式中,我们创建表示各种策略的对象和一个行为随着策略对象改变而改变的 context 对象。策略对象改变 context 对象的执行算法。

1.2 使用场景

1、如果在一个系统里面有许多类,它们之间的区别仅在于它们的行为,那么使用策略模式可以动态地让一个对象在许多行为中选择一种行为。

2、一个系统需要动态地在几种算法中选择一种。

3、如果一个对象有很多的行为,如果不用恰当的模式,这些行为就只好使用多重的条件选择语句来实现。

二、工厂模式

2.1工厂模式定义

工厂模式(Factory Pattern)是 Java 中最常用的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。

工厂模式提供了一种将对象的实例化过程封装在工厂类中的方式。通过使用工厂模式,可以将对象的创建与使用代码分离,提供一种统一的接口来创建不同类型的对象。

在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。

2.2 使用场景

 1、日志记录器:记录可能记录到本地硬盘、系统事件、远程服务器等,用户可以选择记录日志到什么地方。

2、数据库访问,当用户不知道最后系统采用哪一类数据库,以及数据库可能有变化时。

3、设计一个连接服务器的框架,需要三个协议,"POP3"、"IMAP"、"HTTP",可以把这三个作为产品类,共同实现一个接口。

注意事项:作为一种创建类模式,在任何需要生成复杂对象的地方,都可以使用工厂方法模式。有一点需要注意的地方就是复杂对象适合使用工厂模式,而简单对象,特别是只需要通过 new 就可以完成创建的对象,无需使用工厂模式。如果使用工厂模式,就需要引入一个工厂类,会增加系统的复杂度。

三、实现

        需求为用户在美团上购买物品后的核销业务,根据不同核销完成后的返回结果进行核销限制添加积分等,例如是否是本公司app下的会员,是否添加本公司企微,是否关注本公司公众号来进行对应操作,(后续可能会添加其他核销方式),所以该处可利用策略模式实现,而不是一长串的if..else进行功能的实现,违反了开闭原则(对扩展开放,对修改封闭),

3.1 使用if...else实现功能伪代码

        过多嵌套,后续扩展功能不利于维护,实现简单

public class StrategyTest {

    @Resource
    private TbUserDao tbUserDao;

    @Resource
    private TbActivityDao tbActivityDao;

    @Resource
    private MtCouponService mtCouponService;

    @Test
    public Object verify (String userId,String receipt_code) {
        Map<String,Object> result = new HashMap<>();
        //1. 获取用户信息
       TbUser tbUser = tbUserDao.selectUserByUserId(userId);
        if (null == tbUser) {
            return null;
        }
        // 根据receipt_code 和 skuId 查询出需要核销的类型
        // 2. 根据不同的核销类型进行核销
        String callBackMsg = mtCouponService.getCoinByReceiptCode(receipt_code, userId, "杭州");
        Map callBackMap = JSON.parseObject(callBackMsg, Map.class);
        if ("200".equals(callBackMap.get("code"))) {
            //2.1. 判断核销记录增加活动对应参数
            TbActivity activity = new TbActivity();
            activity.setModifyTime(new Date());
            if ("企微".equals(callBackMap.get("type"))) {
                activity.setActivitySource("企微");
                activity.setActivityCurrency(tbUser.getUserCurrency() + 200);
                tbActivityDao.insert(activity);
                result.put("code", "200");
                result.put("msg", "核销成功");
                return result;
            }else  if ("加入企微群".equals(callBackMap.get("type"))) {
                activity.setActivitySource("加群");
                activity.setActivityCurrency(tbUser.getUserCurrency() + 230);
                tbActivityDao.insert(activity);
                result.put("code", "200");
                result.put("msg", "核销成功");
                return result;
            }else  if ("关注公众号".equals(callBackMap.get("type"))) {
                activity.setActivitySource("关注公众号");
                activity.setActivityCurrency(tbUser.getUserCurrency() + 300);
                tbActivityDao.insert(activity);
                result.put("code", "200");
                result.put("msg", "核销成功");
                return result;
            }else  if ("开通会员".equals(callBackMap.get("type"))) {
                activity.setActivitySource("开通会员");
                activity.setActivityCurrency(tbUser.getUserCurrency() + 400);
                tbActivityDao.insert(activity);
                result.put("code", "200");
                result.put("msg", "核销成功");
                return result;
            }
        }
        result.put("code", "505");
        result.put("msg", callBackMap.get("msg"));
        return result;
    }
}

3.2 使用策略模式+工厂模式

        涉及到实际业务,服务实现直接简化为打印相应核销策略

抽象策略类接口

public interface VerificationService {

    String verificationName();

    void verify();
}

实现具体策略服务

@Service
public class OfficialAccountsServiceImpl implements  VerificationService{
    @Override
    public String verificationName() {
        return VerificationEnum.OfficialAccounts.getName();
    }

    @Override
    public void verify() {
        System.out.println("公众号核销 = ");
    }
}
@Service
public class VipVerificationServiceImpl implements  VerificationService{

    @Override
    public String verificationName() {
        return VerificationEnum.Vip.getName();
    }

    @Override
    public void verify() {
        System.out.println("会员核销 = ");
    }
}
@Service
public class WeComGroupServiceImpl implements  VerificationService{
    @Override
    public String verificationName() {
        return VerificationEnum.WeComGroup.getName();
    }

    @Override
    public void verify() {
        System.out.println("企微加群核销 = ");
    }
}
@Service
public class WeComVerificationServiceImpl implements  VerificationService{
    @Override
    public String verificationName() {
        return VerificationEnum.WeCom.getName();
    }

    @Override
    public void verify() {
        System.out.println("企微核销 = ");
    }
}

环境类

/**
 *  核销枚举类
 */
public enum VerificationEnum {
    WeCom( "WeCom"),

    WeComGroup("WeComGroup"),

    OfficialAccounts( "OfficialAccounts"),

    Vip("Vip");

    private String name;

    VerificationEnum(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}
/**
 *  初始化,并获取VerificationService所有服务bean
 */
@Component
@Slf4j
public class StrategyHandler implements InitializingBean, ApplicationContextAware {
    /**
     * 存放策略的map,可以理解为策略的注册中心
     */
    @Resource
    private final Map<String, VerificationService> strategyServiceMap = new ConcurrentHashMap<>(16);
    /**
     * spring的上下文
     */
    private ApplicationContext applicationContext;

    @Override
    public void afterPropertiesSet() {
        //初始化把所有的策略bean放进ioc,使用的时候获取
        Map<String, VerificationService> matchBeans = applicationContext.getBeansOfType(VerificationService.class);
        //策略注入的bean做key,策略实现类做value
        matchBeans.forEach((key, value) -> {
            strategyServiceMap.put(value.verificationName(), value);
            log.info("项目启动时初始化核销策略模式为: key={},value={}", key, value);
        });
    }

    /**
     *
     * @param applicationContext    spring的上下文
     * @throws BeansException 获取bean异常
     */
    @Override
    public void setApplicationContext(@NotNull(message = "bean不能为空") ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    /**
     * 通过key获取对应的策略实现
     *
     * @param verificationName key 核销名称  (String类型或者整形都行,保持和策略接口一致就行)
     * @return VerificationService 具体的核销服务
     */
    public VerificationService  getStrategy(String verificationName) throws RuntimeException{
        if (null == strategyServiceMap.get(verificationName)) {
            //默认策略
            throw  new RuntimeException("核销策略异常...");
        }
        return strategyServiceMap.get(verificationName);
    }



}

        *初始化项目时直接注册服务,方便调用 

        调用方便,易扩展,但是实现过程较繁琐,

public class StrategyTest {

    @Resource
    private StrategyHandler strategyHandler;

    @Test
    public void test() {
 Map<String,Object> result = new HashMap<>();
        //1. 获取用户信息
       TbUser tbUser = tbUserDao.selectUserByUserId(userId);
        if (null == tbUser) {
            return null;
        }
        // 根据receipt_code 和 skuId 查询出需要核销的类型
        // 2. 根据不同的核销类型进行核销
        String callBackMsg = mtCouponService.getCoinByReceiptCode(receipt_code, userId, "杭州");
        Map callBackMap = JSON.parseObject(callBackMsg, Map.class);
        if ("200".equals(callBackMap.get("code"))) {
            //服务实现类里直接实现具体操作
                strategyHandler.getStrategy(callBackMap.get("type")).verify();    
                result.put("code", "200");
                result.put("msg", "核销成功");
                return result;
        }  
        result.put("code", "505");
        result.put("msg", callBackMap.get("msg"));
        return result; 
    }

四 总结

4.1 优点

  • 提高了代码的复用性和可维护性,将算法的定义与其具体实现进行解耦。
  • 可以在运行时动态替换算法,提高了程序的灵活性。
  • 符合开闭原则,新增算法无需修改现有代码。

4.2 缺点

  • 客户端需要知道所有的策略类,并根据具体场景选择合适的策略,增加了客户端的复杂度。
  • 如果策略类较多,会导致类的数量增多,增加系统的复杂度。

4.3 适用场景

  • 当一个系统中存在多个类只有它们的行为或算法不同时。
  • 当一个类定义了多种行为,而这些行为在这个类的操作中以多个条件语句的形式出现,可以将相关的条件分支移入它们各自的策略类中,以替换这些条件语句。
  • 当系统需要动态地在几种算法中选择一种时,如根据不同的配置、用户选择或者环境条件等。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/264965.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

cleanmymac和柠檬清理哪个好 cleanmymac有必要买吗

大家好&#xff0c;不定期分享正版软件激活安装、使用帮助&#xff0c;售后等知识。 在我们的日常使用中&#xff0c;电脑常常会出现卡顿、运行缓慢的情况。这时候&#xff0c;我们通常会想到清理电脑&#xff0c;以期望恢复电脑的正常运行状态。而在清理电脑时&#xff0c;有两…

Service详解【六】

文章目录 6. Service详解6.1 Service介绍6.2 Service类型6.3 Service使用6.3.1 实验环境准备6.3.2 ClusterIP类型的Service6.3.3 HeadLiness类型的Service6.3.4 NodePort类型的Service6.3.5 LoadBalancer类型的Service6.3.6 ExternalName类型的Service 6.4 Ingress介绍6.5 Ingr…

多个bean获取同一个Service,获取的内存地址是同一块;引用bean地址存储在一个map中

public class UserService {public void test() {System.out.println("---test----");} } Testpublic void doesNotContain1(){// 创建Spring容器AnnotationConfigApplicationContext applicationContext new AnnotationConfigApplicationContext();// 向容器中注册…

3 pandas之dataframe

定义 DataFrame是一个二维数据结构&#xff0c;即数据以行和列的方式以表格形式对齐。 DataFrame特点&#xff1a; 存在不同类型的列大小可变带有标签的轴可对列和行进行算数运算 构造函数 pandas.DataFrame( data, index, columns, dtype, copy)参数解释&#xff1a; 序号…

【SpringBoot快速入门】(1)SpringBoot的开发步骤、工程构建方法以及工程的快速启动详细讲解

目录 SpringBoot简介1 SpringBoot快速入门1.1 开发步骤1.1.1 创建新模块1.1.2 创建 Controller1.1.3 启动服务器1.1.4 进行测试 2 对比3 官网构建工程3.1 进入SpringBoot官网3.2 选择依赖3.3 生成工程 4 SpringBoot工程快速启动4.1 问题导入4.2 打包4.3 启动 之前我们已经学习的…

浅谈Redis分布式锁(上)

作者简介&#xff1a;大家好&#xff0c;我是smart哥&#xff0c;前中兴通讯、美团架构师&#xff0c;现某互联网公司CTO 联系qq&#xff1a;184480602&#xff0c;加我进群&#xff0c;大家一起学习&#xff0c;一起进步&#xff0c;一起对抗互联网寒冬 不论面试还是实际工作中…

Netty 与 RPC(一)

Netty 与 RPC Netty 原理 Netty 是一个高性能、异步事件驱动的 NIO 框架&#xff0c;基于 JAVA NIO 提供的 API 实现。它提供了对TCP、UDP 和文件传输的支持&#xff0c;作为一个异步 NIO 框架&#xff0c;Netty 的所有 IO 操作都是异步非阻塞的&#xff0c;通过 Future-List…

算法练习Day19 (Leetcode/Python-二叉树)

108. Convert Sorted Array to Binary Search Tree Given an integer array nums where the elements are sorted in ascending order, convert it to a height-balanced binary search tree. 思路&#xff1a; 一个高度平衡二叉树是指一个二叉树每个节点 的左右两个子树的…

开启虚拟与现实的融合时代

数字人直播&#xff0c;作为一项新兴技术&#xff0c;正逐渐改变着我们的生活方式和沟通方式。它通过利用最先进的人工智能技术&#xff0c;使得虚拟形象得以与现实世界实时互动&#xff0c;为用户带来了全新的体验。本文将探讨数字人直播的意义、应用场景以及可能带来的影响。…

利用MultCloud在线复制传输不同网盘之间的数据:支持谷歌Drive、百度网盘等

本文介绍通过MultCloud平台&#xff0c;在国内实现谷歌Drive、OneDrive、百度网盘等不同云盘之间数据的传输、共享等操作的免费方法。 有的时候&#xff0c;我们希望对自己不同网盘之间的数据加以传输、共享&#xff1b;例如&#xff0c;我们可以将自己谷歌Drive中的数据&#…

Deep learning-based small object detection: A survey(2023)

文章目录 AbstractIntroductionContribution Generic SOD algorithms提高输入特征的分辨率&#xff08;Most Important&#xff09;Methods 尺度感知训练Methods 融合上下文信息Methods 数据增强Methods 其他策略Methods 关键的SOD任务小人脸检测Methods 小型行人检测Methods 航…

在线考试系统-软件与环境

一. 软件 1.Navicat、phpstudy、Idea、Vsode 参考 网盘链接 二.配置文件 1.NodeJS、JDK、Mysql 参考 网盘链接 注意点&#xff1a; 1.Mysql 切记需要环境变量配置 2.数据库密码要好记点的&#xff0c;别乱设 3.环境变量配置的路径要能找到 三.安装运行 1.下载网盘内的软件&am…

【ps】常见工具说明

1&#xff1a;污点修复工具 》内容识别 去除脸部珍珠 》创建纹理 2&#xff1a;修复画笔工具 按住ALT选择一处你想移动的地方 3&#xff1a;修补工具 》内容识别 选择一个区域 将区域内的东西移动到 另一处皮肤上

【Linux笔记】系统信息

&#x1f34e;个人博客&#xff1a;个人主页 &#x1f3c6;个人专栏&#xff1a;Linux学习 ⛳️ 功不唐捐&#xff0c;玉汝于成 目录 前言 命令 1. uname - 显示系统信息 2. hostname - 显示或设置系统主机名 3. top - 显示系统资源使用情况 4. df - 显示磁盘空间使用情…

Socket.D 基于消息的响应式应用层网络协议

首先根据 Socket.D 官网 的副标题&#xff0c;Socket.D 的自我定义是&#xff1a; 基于事件和语义消息流的网络应用协议。官网定义的特点是&#xff1a; 基于事件&#xff0c;每个消息都可事件路由所谓语义&#xff0c;通过元信息进行语义描述流关联性&#xff0c;有相关的消…

【前端】前后端通信方法与差异(未完待续)

系列文章 【Vue】vue增加导航标签 本文链接&#xff1a;https://blog.csdn.net/youcheng_ge/article/details/134965353 【Vue】Element开发笔记 本文链接&#xff1a;https://blog.csdn.net/youcheng_ge/article/details/133947977 【Vue】vue&#xff0c;在Windows IIS平台…

Ubuntu 常用命令之 netstat 命令用法介绍

&#x1f4d1;Linux/Ubuntu 常用命令归类整理 netstat 是一个非常有用的命令行工具&#xff0c;它可以帮助我们监控和诊断网络问题。在 Ubuntu 系统中&#xff0c;我们可以使用 netstat 命令来查看网络连接、路由表、接口统计等信息。 netstat 命令的参数有很多&#xff0c;以…

如何用CHAT做调研?

问CHAT&#xff1a;高校之间关于艺术教育专业调研情况 CHAT回复&#xff1a;对于高校之间的艺术教育专业调研&#xff0c;以下几个方面可能会涉及&#xff1a; 1. 课程设计和学分&#xff1a;可以调研不同高校艺术教育专业的课程设置&#xff0c;包括专业核心课程、相关选修课…

论文润色的注意事项有哪些 papergpt

大家好&#xff0c;今天来聊聊论文润色的注意事项有哪些&#xff0c;希望能给大家提供一点参考。 以下是针对论文重复率高的情况&#xff0c;提供一些修改建议和技巧&#xff0c;可以借助此类工具&#xff1a; 标题&#xff1a;论文润色的注意事项――确保论文质量与表达的关键…

Leetcode 406 根据身高重建队列

题意理解&#xff1a; people [[7,0],[4,4],[7,1],[5,0],[6,1],[5,2]] 给定一个二维数组&#xff0c;&#xff08;h,k&#xff09;h表示此人身高&#xff0c;k表示前面有几个人比他高。 我们按照每个人的h,k两个维度的需求给每个人排在合适的位置。 如&#xff1a; [5,0][7,0]…