真实业务场景使用-模板模式+策略模式组合

news/2024/10/31 3:28:59/

模板和策略设计模式一般是使用最频繁的设计模式,模板的场景主要是处理一系列相同的流程,将这些流程放到模板里,每个流程里的处理可能有一些不一样的地方,则可以抽象出一个方法,由每一个有实际意义的子类实现。

策略模式:由于总会有不同的实现类,而最终总会要调用实现里,所以用策略模式帮助我们如何调用到实现类里。

1.背景说明

以我自己亲身经历的场景说一下,我接手了之前写好的系统,但是有个问题,一个新建功能,在还没有保存前就先获取了编号,不入库不记录,导致会出现很多人进入相同页面也就分配相同的编号,有一个保存被占用后,其他都无法保存因为编号重复,查看整个系统后发现很多都是这种方式,所以需要优化一下。

思考了一下,业务逻辑是需要记录一个编号值的库表,然后类别不同,再有一个编码值就可以,也就是如下这种方式,设计了如下库表

codetype
20220102goods:商品编码
CD20230205biss:业务编码
23order:序号码值

每次生成编码则将对应的类型的code更新,这样库里永远是新的,处理共性业务就会出现,

1.获取库里当前类型的编码,

2.根据当前编码的值得到新的编码(前一个编码值+1或者其他规则),

3.得到新的编码值则更新到库里,

所有的生成编号逻辑都要经历这三步,那么就抽出来共性用模板方式,唯一不同的是生成新的编码规则,看表里有的是序号,有的是年月日,有的是前缀+年月日,所以生成编码的方法需要抽象化由各个子类实现。

流程出来了,现在看来已经有三个子类了,我们如何在用户使用时精准使用某个具体类呢,这里就采用策略模式,由于策略模式会出现多个if判断的情况,所以这里对策略模式进行优化,采取map映射key获取具体的实现类。

2.UML类图

还是蛮简单的,看下面的UML图,就涉及到几个类,CodeStrategy则是策略模式,启动时将不同实例存储到map里,使用时从map获取实例,

CodeProcessAbstract为抽象类,除了共有方法在这里实现外,不同实现类不同规则则在此类进行抽象化方法,留给子类实现即可

GoodsCode以及OrderCode则是实现类,此只实现抽象类的抽象方法,用来处理不同规则处理,还是蛮简单的,大家可以仔细看下并对下代码

3.代码实现

3.1 模板模式

抽象类定义编码生成流程:

其中getCreateCode方法则是模板模式的体现,getPreCode是所有子类都可以使用的(因为根据不同类型查询码值),所以在此类里直接定义私有方式实现。

第二个是generateCode方法,这个方法在此类里只是定义抽象方法不实现,原因是每一个的子类实现是不一样的规则,所以这里交给具体子类实现即可。

第三个方法是updateNewCode,此方法也是所有子类共有可使用,所以在此类里实现即可,所有的都可以通过类型更新对应的新的码值即可,

public abstract class CodeProcessAbstract {// 模板模式处理流程public String getCreateCode(String codeType){// 获取前一个code码String preCode=getPreCode(codeType);// 根据旧编号生成新的编号String newCode=generateCode(preCode);// 将新的编号更新到库里updateNewCode(newCode,codeType);return newCode;}private String getPreCode(String codeType){// 假装从库里取出对应类型的codeSystem.out.println("从库里取出码值:20230504001或1|"+codeType);return "20230504001";}protected abstract String generateCode(String preCode);private void updateNewCode(String newCode,String codeType){// 假装将新的编码入库System.out.println("将"+codeType+"更新code码为:"+newCode);}
}

 商品编号生成码类,继承 CodeProcessAbstract类,实现generateCode来生成新的编码,假如这个规则是年月日+序号,就需要将前一个编码值传入根据后几位序号+1

@Component
public class GoodsCode extends CodeProcessAbstract {@Overrideprotected String generateCode(String preCode){// 模拟商品规则生成System.out.println("生成新的编号:20230504002");return "20230504002";}
}

序号生成码类,继承CodeProcessAbstract,实现自己的生成规则

@Component
public class OrderCode extends CodeProcessAbstract {@Overrideprotected String generateCode(String preCode) {// 模拟序号生成编号System.out.println("生成新的编号:20230504002");return "2";}
}

还有别的一系列的子类,咱们先不一一介绍了,都是这样的方式。

这样执行流程我们定义完了,我们有了获取编码,生成编码,更新编码的能力,但是我们怎么去统一调用它呢,如果这个类用这个实现类,那个类用那个实现类,等到实现类一多是不是很混乱呢,对于用户来说更喜欢简洁的呀,所以这里我们用一个类来统一入口

3.2 策略模式

其实很简单,定义一个全局变量Map类型的用来存储各个子类实例的,子类实例采用Spring管理以及获取,然后在spring加载以后调用注册方法,则将两个子实例加载到map里,再用户使用时根据类型获取实例即可。

@Component
public class CodeStrategy {@Resourceprivate  GoodsCode goodsCode;@Resourceprivate  OrderCode orderCode;private final Map<String,CodeProcessAbstract> instanceMap=new HashMap(2);// 项目启动即可注册实例@PostConstructpublic void register(){instanceMap.put(CodeTypeEnum.GOODS_NO.getCode(),goodsCode);instanceMap.put(CodeTypeEnum.ORDER_NO.getCode(),orderCode);}// 获取实例public CodeProcessAbstract getInstance(String codeType){return instanceMap.get(codeType);}}

CodeTypeEnum枚举类

public enum CodeTypeEnum{GOODS_NO("goods_no","商品编号"),ORDER_NO("order_no","序号");private String code;private String name;private CodeTypeEnum(String code,String name){this.code=code;this.name=name;}public  String getCode(){return code;}public  String getName(){return name;}
}

4.使用

使用方式:单元测试如下

@SpringBootTest
public class TestStrategyMooban {@Resourceprivate  CodeStrategy codeStrategy;@Testvoid useMS() {GoodsCode goods=(GoodsCode)codeStrategy.getInstance(CodeTypeEnum.GOODS_NO.getCode());String newCode=goods.getCreateCode(CodeTypeEnum.GOODS_NO.getCode());System.out.println("最终商品编号:"+newCode);OrderCode order=(OrderCode)codeStrategy.getInstance(CodeTypeEnum.ORDER_NO.getCode());String newCode1=order.getCreateCode(CodeTypeEnum.ORDER_NO.getCode());System.out.println("order序号编号:"+newCode1);}
}

 测试结果,我们可以看到只需要引用一个策略类即可调用不同的实例处理流程,每个流程都是一样的,只不过具体生成编码策略不同,这样代码是不是清晰了很多

 


http://www.ppmy.cn/news/62800.html

相关文章

C# 判断文件/目录是否存在

C# 判断文件是否存在&#xff0c;不存在则创建 C#判断指定目录是否存在&#xff0c;不存在就创建 spath&#xff1a;文件夹路径名 using System.IO; if (Directory.Exists(spath)) { } else { DirectoryInfo directoryInfo new DirectoryInfo(spath); directoryInfo.…

Linux学习之Shell(一)

Shell概述 1&#xff09;Linux提供的Shell解析器有 [xiaominghadoop101 ~]$ cat /etc/shells /bin/sh /bin/bash /sbin/nologin /usr/bin/sh /usr/bin/bash /usr/sbin/nologin /bin/tcsh /bin/csh2&#xff09;bash和sh的关系 [xiaominghadoop101 bin]$ ll | grep bash -rwxr…

ShardingProxy分库分表实战

目录 ShardingProxy简述 快速使用 ShardingProxy部署 ShardingProxy使用 ShardingProxy的服务治理 Shardingproxy的其他功能 ShardingProxy的SPI扩展 ShardingSphere总结 ShardingProxy简述 ShardingProxy的功能同样是分库分表&#xff0c;但是他是一个独立部署的服务…

实现docker目录和本地目录的互通

1.docker run使用-v 绑定一个卷 来实现容器中的目录与本地某个目录的联通&#xff0c;这样可以让容器中的数据在容器销毁之后仍然可以被保留下来。下面是一些基本的步骤&#xff1a; 创建一个本地目录&#xff0c;用于存储容器中的数据。例如&#xff0c;我们可以在本地创建一…

Altium Designer中如何在顶层中添加对应端口

转载说明&#xff1a; 大众深度科普 https://jingyan.baidu.com/article/c33e3f4889f327ea15cbb584.html 版权归原作者所有&#xff1b;感谢原作者的分享&#xff1b; 转载到此&#xff0c;主要为了后期查看方便&#xff1b; 本经验简要介绍Altium Designer中如何在顶层中添…

字符设备驱动开发实验

我们以 chadev 这个虚拟设备为 例&#xff0c;完整的编写一个字符设备驱动模块。chadev 不是实际存在的一个设备&#xff0c;是为了方 便讲解字符设备的开发而引入的一个虚拟设备设备有两个缓冲区&#xff0c;一个为读缓冲 区&#xff0c;一个为写缓冲区&#xff0c;这两个缓冲…

(六)【平衡小车制作】位置式PID、直立环与速度环编程

本篇文章我将针对位置式PID算法、直立环、速度环等的编程进行详细的讲解&#xff0c;让每位小伙伴能够对这三个概念的编程逻辑有更加清晰的理解。 一、直立环&#xff08;PD控制器&#xff09; 1.中文公式  直立环输出Kp1角度偏差Kd角度偏差的微分  // 角度偏差真实角度-期…

JMeter开发web及手机APP自动化脚本练习

&#xff08;一&#xff09;开发web自动化脚本练习 一、打开浏览器代理服务器设置 我这里用的是360浏览器&#xff0c;打开浏览器代理服务器设置&#xff0c;端口要与jmeter中的端口设置保持一致哦。 二、JMeter设置代理 JMeter设置代理&#xff08;jmeter中的端口要与360浏览…