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

news2025/1/24 11:39:49

一、策略模式

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 适用场景

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

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

python画图【03】泰坦尼克号数据分析

导包 import numpy as np import pandas as pdimport matplotlib import matplotlib.pyplot as plt %matplotlib inlineplt.rcParams[font.sans-serif] "Microsoft YaHei"import seaborn as sns加载数据集 titanic sns.load_dataset("titanic")titanic…

pip 常用指令 pip cache 命令用法介绍

&#x1f4d1;pip 常用命令归类整理 pip cache 是一个用于管理pip缓存的命令。pip是Python的包管理器&#xff0c;用于安装和管理Python包。当你使用pip安装一个包时&#xff0c;pip会首先在其缓存中查找该包。如果在缓存中找到&#xff0c;pip将从缓存中安装该包&#xff0c;…

HarmonyOS布局之scroll

对于Scroll 布局官方讲解非常好&#xff0c;我在这做个总结&#xff0c;主要结合实际应用进行补充 Scroll — 首先他是一个容器布局&#xff0c;所有的容器布局都可包含子布局&#xff0c;因此scroll 布局也可包含子组件但是&#xff08;他只能包含一个子组件&#xff09;&…

wordpress主题modown v8.81+erphpdown v16.0无限制无授权开心版

修复bug&#xff08;v8.81 2023.03.07&#xff09; 新增文章页正文下面常见问题手风琴模块&#xff0c;可设置显示文章的更新日期而不是发布日期&#xff0c;首页幻灯片支持指定文章、支持一个大图4个小图显示&#xff0c;grid网格列表支持显示简介&#xff0c;前台个人中心里显…

Odoo16 实用功能之在Tree视图的记录中加入按钮

Tree视图中添加按钮&#xff0c;通常使用<button>标签 只能有一列来显示这些按钮 代码示例&#xff1a; <tree><field name"name" /><button name"test001" class"text-warning" type"object" string"Resc…

NHANES数据库周报(12.6)

郑老师统计课程&#xff0c;欢迎点击报名&#xff1a;Nhanes公共数据库挖掘 课程 美国国家健康和营养检查调查&#xff08;NHANES&#xff09;是一项旨在评估美国成人和儿童健康和营养状况的研究计划。该调查的独特之处在于它结合了访谈和体格检查。由美国疾病控制和预防中心&a…

Java日志框架Logback

logback.xml文件配置(放在src下微服务建议放在resources下) <?xml version"1.0" encoding"UTF-8"?> <configuration><!--定义日志文件的存储地址,使用绝对路径--><property name"LOG_HOME" value"d:/logs"/>…

【Linux笔记】系统信息

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

eclipse中基于maven构建的web项目pom.xml中指定的jar包无法发布到tomcat中

eclipse运行maven web项目报错&#xff1a; 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 信息: Star…

EDA实验-----直流电机驱动设计(Quartus II )

目录 一、实验目的 二、实验仪器设备 三、实验的重点和难点 四、实验原理 五、实验步骤 六、实验报告 七、实验过程 1.分频器代码 2.方向选择器 3.直流电动机工作原理 4.电路连接图 5.文件烧录 一、实验目的 了解直流电机控制的工作原理和实现的方法。掌握PWM波控…

农学小作业(根据降雨量计算排灌水量)(旱地版)

//------------------------------------------------------------------------------------------------------------------- //农学小作业(根据降雨量计算排灌水量)(旱地版) //01.参考书目:ISBN-7-80734-087-8.ISBN-7-80124-644-6. //01.农作物生长发育阶段需要消耗水份. //0…

Deepin更换仿Mac主题

上一篇博客说了要写一篇deepin系统的美化教程 先看效果图&#xff1a; 准备工作&#xff1a; 1.你自己 嘻嘻嘻 2.能上网的deepin15.11电脑 首先去下载主题 本次需要系统美化3部分&#xff1a;1.图标 2.光标 3.壁纸 开始之前&#xff0c;请先把你的窗口特效打开&#xff0c;…

【MVT_1703230471】基于Python NLTK分词、词云、LDA主题分类及GPT情感分类

【Talk is cheap】 1 数据探索 case idcase outcome case title case text 0 Case1 cited Alpine Hardwood (Aust) Pty Ltd v Hardys Pty Lt... Ordinarily that discretion will be exercised s... 1 Case2 cited Black v Lipovac [1998] FCA 699 ; (1998) 217 AL... The gen…

Epson爱普生手臂机器人与PC通讯 C#

一、Epson手臂配置 1.安装Epson手臂控制软件 安装Epson手臂控制软体EPSON RC+ 7.0(根据实际需求下载应用),可以去官网下载安装。 2.硬件配置 准备一台PC,用网线连接PC和EPSON手臂控制器。 3.在PC上修改IP地址 EPSON手臂默认IP(192.168.0.1),PC IP改为手臂同一网段…

歌曲春节回家:荆涛歌声中的深情与诗人的心声

歌曲春节回家&#xff1a;荆涛歌声中的深情与诗人的心声 在寒冬的寂静中&#xff0c;荆涛的歌声如同春风拂面&#xff0c;温暖而深情。它唤起了我们对家的思念&#xff0c;让我们感受到家的温暖和亲情的珍贵。而在歌声中&#xff0c;我们仿佛听到了那些描绘春节的经典诗句&…

pycharm修改项目文件夹名称

目录 1 修改项目文件夹名称 2 修改代码中的项目名称 1 修改项目文件夹名称 选中项目文件夹&#xff0c;右键&#xff0c;选择refactor-rename。 选择rename project&#xff1a; 然后输入新的项目名称。 此时进入资源管理器&#xff0c;修改项目文件夹的名字&#xff0c;完成…

Linux 一键部署二进制Gitea

gitea 前言 Gitea 是一个轻量级的 DevOps 平台软件。从开发计划到产品成型的整个软件生命周期,他都能够高效而轻松的帮助团队和开发者。包括 Git 托管、代码审查、团队协作、软件包注册和 CI/CD。它与 GitHub、Bitbucket 和 GitLab 等比较类似。 Gitea 最初是从 Gogs 分支而来…

EarMaster Pro 7 简体中文破解版 v7.2.0.42 电脑版

软件介绍 EarMaster破解版一款功能强大的专业级别多媒体音乐教育学习软件&#xff0c;EarMaster破解版提供了大量音乐相关的学习内容&#xff0c;用户在这里可以学习基础的和弦、音阶、节奏&#xff0c;也可以提升自己的音感&#xff0c;如果基础已经很扎实了&#xff0c;还可…

vue3 + TypeScript使用国际化

vue3 TypeScript使用国际化 本文使用了 Vite 构建工具创建的vue3项目Vite 支持使用特殊的 import.meta.glob 函数从文件系统导入多个模块Vite 官方中文文档当然如果你的vue3项目未使用vite,你也可以为你的旧项目提提速&#xff0c;安装vite &#xff0c;安装方法在上一个博客…

ubuntu 18.04 共享屏幕

用于windows远程ubuntu 1. sudo apt install xrdp 2. 配置 sudo vim /etc/xrdp/startwm.sh 把最下面的test和exec两行注释掉&#xff0c;添加一行 gnome-session 3.安装dconf-editor : sudo apt-get install dconf-editor 关闭require encrytion org->gnome->desktop…