设计模式在实际业务中应用 - 模版方法
1. 业务背景
作者在工作中主要主导 A 业务线的系统建设,A 业务线主要是零售场景酒水的售卖与即时配送服务。为了方便运营在自研系统中对多平台商品进行管理而开发的三方平台商品管理功能,本次介绍的模版方法模式则是在该功能开发过程中的落地实践。
2. 技术方案选型
该业务场景可选设计为三种:
-
自研系统根据自身业务形态对商品领域进行抽象建模,在自研系统里对商品领域的操作最终映射到三方平台;
-
自研系统对三方平台商品进行抽象建模,保存三方平台商品在某个时刻的快照,所有操作均是对快照的操作,进而最终映射到三方平台;
-
自研系统充当一个适配器和转发层,所有对商品的操作都直接映射到三方平台;
从系统建设角度来说最好的选择是第一种,但是对于一个业务方想要更快的提升运营效率来说,更好的选择是第二种和第三种,我们从自身业务情况、性能、后续向第一种方案演进的角度选择了第二种方案。
3. 模版方法应用
以下代码仅为演示对模版方法的应用和展示代码设计思路,有部分方法并未实现,但通过注释说明方法内做了什么操作。
3.1. 模版抽象类
@Component
public abstract class ThirdPlatformStoreGoodsOperations {/*** 商品上架* @param storeGoodsShelvesParam 商品上架参数* @return 新商品记录ID*/public Long shelves(StoreGoodsShelvesParam storeGoodsShelvesParam) {// 获取门店本地商品ThirdPlatformGoods localStoreGoods = this.getLocalStoreGoods(storeGoodsShelvesParam.getId());if (localStoreGoods == null) {throw new HistoricalDataException("商品数据已更新,请刷新当前页面");}// 上架三方平台门店商品this.shelvesThirdPlatformStoreGoods(localStoreGoods);// 刷新本地商品StoreGoodsRefreshParam storeGoodsRefreshParam = new StoreGoodsRefreshParam();BeanUtils.copyProperties(storeGoodsShelvesParam, storeGoodsRefreshParam);return this.refreshOne(storeGoodsRefreshParam);}/*** 商品下架* @param storeGoodsWithdrawParam 商品下架参数* @return 新商品记录ID*/public Long withdraw(StoreGoodsWithdrawParam storeGoodsWithdrawParam) {// 获取门店本地商品ThirdPlatformGoods localStoreGoods = this.getLocalStoreGoods(storeGoodsWithdrawParam.getId());if (localStoreGoods == null) {throw new HistoricalDataException("商品数据已更新,请刷新当前页面");}// 下架三方平台门店商品this.withdrawThirdPlatformStoreGoods(localStoreGoods);// 刷新本地商品StoreGoodsRefreshParam storeGoodsRefreshParam = new StoreGoodsRefreshParam();BeanUtils.copyProperties(storeGoodsWithdrawParam, storeGoodsRefreshParam);return this.refreshOne(storeGoodsRefreshParam);}/*** 增加商品库存* @param storeGoodsIncreaseStockParam 增加商品库存参数* @return 新商品记录ID*/public Long increaseStock(StoreGoodsIncreaseStockParam storeGoodsIncreaseStockParam) {// 获取门店本地商品ThirdPlatformGoods localStoreGoods = this.getLocalStoreGoods(storeGoodsIncreaseStockParam.getId());if (localStoreGoods == null) {throw new HistoricalDataException("商品数据已更新,请刷新当前页面");}// 增加三方平台店铺商品库存this.increaseThirdPlatformStoreGoodsStock(storeGoodsIncreaseStockParam, localStoreGoods);// 刷新本地商品StoreGoodsRefreshParam storeGoodsRefreshParam = new StoreGoodsRefreshParam();BeanUtils.copyProperties(storeGoodsIncreaseStockParam, storeGoodsRefreshParam);return this.refreshOne(storeGoodsRefreshParam);}/*** 增加商品库存* @param storeGoodsDecreaseStockParam 减少商品库存参数* @return 新商品记录ID*/public Long decreaseStock(StoreGoodsDecreaseStockParam storeGoodsDecreaseStockParam) {// 获取门店本地商品ThirdPlatformGoods localStoreGoods = this.getLocalStoreGoods(storeGoodsDecreaseStockParam.getId());if (localStoreGoods == null) {throw new HistoricalDataException("商品数据已更新,请刷新当前页面");}// 减少三方平台店铺商品库存this.decreaseThirdPlatformStoreGoodsStock(storeGoodsDecreaseStockParam, localStoreGoods);// 刷新本地商品StoreGoodsRefreshParam storeGoodsRefreshParam = new StoreGoodsRefreshParam();BeanUtils.copyProperties(storeGoodsDecreaseStockParam, storeGoodsRefreshParam);return this.refreshOne(storeGoodsRefreshParam);}/*** 刷新店铺单个商品* @param storeGoodsRefreshParam 刷新店铺单个商品参数* @return 新商品记录ID*/public Long refreshOne(StoreGoodsRefreshParam storeGoodsRefreshParam) {// 获取门店本地商品ThirdPlatformGoods localStoreGoods = this.getLocalStoreGoods(storeGoodsRefreshParam.getId());if (localStoreGoods == null) {throw new HistoricalDataException("商品数据已更新,请刷新当前页面");}// 获取三方门店商品StoreGoodsInfoResult storeGoodsInfoResult = this.getThirdPlatformStoreGoods(storeGoodsRefreshParam, localStoreGoods);// 刷新本地商品return this.refreshLocalStoreGoods(storeGoodsRefreshParam, storeGoodsInfoResult);}/*** 刷新店铺商品* @param storeGoodsRefreshParam 刷新店铺单个商品参数* @param storeGoodsInfoResult 三方门店商品*/private Long refreshLocalStoreGoods(StoreGoodsRefreshParam storeGoodsRefreshParam,StoreGoodsInfoResult storeGoodsInfoResult) {// 更新本地商品}/*** 获取本地店铺商品信息* @param localStoreGoodsId 本地商品ID* @return 本地店铺商品信息*/private ThirdPlatformGoods getLocalStoreGoods(Long localStoreGoodsId) {// 获取本地商品}/*** 上架三方店铺商品* @param localStoreGoods 本地店铺商品信息*/protected abstract void shelvesThirdPlatformStoreGoods(ThirdPlatformGoods localStoreGoods);/*** 下架三方店铺商品* @param localStoreGoods 本地店铺商品信息*/protected abstract void withdrawThirdPlatformStoreGoods(ThirdPlatformGoods localStoreGoods);/*** 获取三方平台店铺商品* @param storeGoodsRefreshParam 刷新店铺单个商品参数* @param localStoreGoods 本地商品* @return 三方门店商品*/protected abstract StoreGoodsInfoResult getThirdPlatformStoreGoods(StoreGoodsRefreshParam storeGoodsRefreshParam,ThirdPlatformGoods localStoreGoods);/*** 增加三方平台店铺商品库存* @param storeGoodsIncreaseStockParam 增加三方平台店铺商品库存参数* @param localStoreGoods 本地店铺商品信息*/protected abstract void increaseThirdPlatformStoreGoodsStock(StoreGoodsIncreaseStockParam storeGoodsIncreaseStockParam,ThirdPlatformGoods localStoreGoods);/*** 减少三方平台店铺商品库存* @param storeGoodsDecreaseStockParam 减少三方平台店铺商品库存参数* @param localStoreGoods 本地店铺商品信息*/protected abstract void decreaseThirdPlatformStoreGoodsStock(StoreGoodsDecreaseStockParam storeGoodsDecreaseStockParam,ThirdPlatformGoods localStoreGoods);/*** 获取对应平台* @return 平台枚举*/protected abstract ThirdPlatformEnums getPlatform();
}
3.2. 模版实现类
public class ELEStoreGoodsTemplate extends ThirdPlatformStoreGoodsOperations {@Overrideprotected void shelvesThirdPlatformStoreGoods(ThirdPlatformGoods localStoreGoods) {// 饿了么平台商品上架}@Overrideprotected void withdrawThirdPlatformStoreGoods(ThirdPlatformGoods localStoreGoods) {// 饿了么平台商品下架}@Overrideprotected StoreGoodsInfoResult getThirdPlatformStoreGoods(StoreGoodsRefreshParam storeGoodsRefreshParam,ThirdPlatformGoods localStoreGoods) {// 获取饿了么平台商品信息return null;}@Overrideprotected void increaseThirdPlatformStoreGoodsStock(StoreGoodsIncreaseStockParam storeGoodsIncreaseStockParam,ThirdPlatformGoods localStoreGoods) {// 增加饿了么平台商品库存}@Overrideprotected void decreaseThirdPlatformStoreGoodsStock(StoreGoodsDecreaseStockParam storeGoodsDecreaseStockParam, ThirdPlatformGoods localStoreGoods) {// 减少饿了么平台商品库存}@Overrideprotected ThirdPlatformEnums getPlatform() {return ThirdPlatformEnums.ELEMO;}
}
4. 与 Spring 结合管理模版实现类
4.1. 基于 Spring IoC 容器依赖查找管理模版
@Component
public class ThirdPlatformGoodsTemplateManage {public Map<ThirdPlatformEnums, ThirdPlatformStoreGoodsOperations> templateMap = new HashMap<>(5);public ThirdPlatformGoodsTemplateManage(ListableBeanFactory listableBeanFactory){ObjectProvider<ThirdPlatformStoreGoodsOperations> beanProvider = listableBeanFactory.getBeanProvider(ThirdPlatformStoreGoodsOperations.class);for (ThirdPlatformStoreGoodsOperations template : beanProvider) {templateMap.put(template.getPlatform(), template);}}public ThirdPlatformStoreGoodsOperations getTemplate(ThirdPlatformEnums thirdPlatformEnum) {ThirdPlatformStoreGoodsOperations thirdPlatformStoreGoodsOperations = templateMap.get(thirdPlatformEnum);Assert.notNull(thirdPlatformStoreGoodsOperations, String.format("%s平台模版未找到!", thirdPlatformEnum.getDesc()));return thirdPlatformStoreGoodsOperations;}public ThirdPlatformStoreGoodsOperations getTemplate(String platformCode) {ThirdPlatformEnums thirdPlatformEnum = ThirdPlatformEnums.getInstanceByCode(platformCode);ThirdPlatformStoreGoodsOperations thirdPlatformStoreGoodsOperations = templateMap.get(thirdPlatformEnum);Assert.notNull(thirdPlatformStoreGoodsOperations, String.format("%s平台模版未找到!", platformCode));return thirdPlatformStoreGoodsOperations;}
}
4.2. 基于 BeanPostProcessor + 注解方式管理模版
@Component
public class ThirdPlatformStoreGoodsOperationsInitialization implements BeanPostProcessor {@Resourceprivate ThirdPlatformGoodsTemplateManage thirdPlatformGoodsTemplateManage;@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {return bean;}@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {if (bean instanceof ThirdPlatformStoreGoodsOperations && bean.getClass().isAnnotationPresent(ThirdPlatformStoreGoodsOperationsTemplate.class)){ThirdPlatformStoreGoodsOperations thirdPlatformStoreGoodsOperations = (ThirdPlatformStoreGoodsOperations) bean;thirdPlatformGoodsTemplateManage.templateMap.put(thirdPlatformStoreGoodsOperations.getPlatform(), thirdPlatformStoreGoodsOperations);}return bean;}
}
改进建议: 模版CODE可通过注解属性获取
5. 为什么选择模版方法
看完代码实现再回过头来,说说选择模版方法的原因:
- 从功能角度来说对三方平台商品管理无非就是那么几个商品新增、商品编辑、商品库存调整、商品上下架等;
- 从商品领域建模对三方平台商品领域行为抽象可分为:创建、修改、增加库存、减少库存、覆盖库存、上架、下架等;
- 从代码复用的角度来说不同平台商品的操作不同的只有最终调用三方平台的差异上,其他代码都可复用,假设第一次开发只先适配美团,那么在美团适配后复用的代码理论上不需要做任何修改;
- 从代码可扩展角度来说,如果增加其他平台,仅需要继承模版抽象类,实现抽象方法即可;
- 从关注点分离角度来说后续适配其他平台仅仅需要关注如何操作三方平台即可,这对新人或者不了解的人来继续迭代有很大好处,关注点集中在如何适配三方平台上;
相关文章:
设计模式在实际业务中应用 - 模版方法
1. 业务背景 作者在工作中主要主导 A 业务线的系统建设,A 业务线主要是零售场景酒水的售卖与即时配送服务。为了方便运营在自研系统中对多平台商品进行管理而开发的三方平台商品管理功能,本次介绍的模版方法模式则是在该功能开发过程中的落地实践。 2.…...

BGP综合实验
任务如下: 1.AS1存在两个环回,一个地址为192.168.1.0/24该地址不能在任何协议中宣告 AS3存在两个环回,一个地址为192.168.2.0/24该地址不能在任何协议中宣告,最终要求这两个环回可以互相通讯 2.整个AS2的IP地址为172.16.0.0/16&…...

Global Surface Summary of the Day 全球逐日气象站点数据 GSOD数据集
数据名称 Global Surface Summary of the Day 数据内容 数据包含以下气象要素的日值观测数据: 气压:平均气压、海平面气压;气温:平均气温、日最高气温、日最低气温;湿度:露点温度(需自行换算…...

Harmony OS4开发入门
代码地址: https://gitee.com/BruceLeeAdmin/harmonyos/tree/master 项目目录介绍 ArkTS介绍 简单案例: State times: number 0/*数据类型:stringnumberany: 不确定类型,可以是任意类型*/State msg: string "hello"…...
.net core 事务
在 .NET Core 中,可以使用 Entity Framework Core 来实现事务处理。下面是一个简单的示例,展示了如何在 .NET Core 中使用 Entity Framework Core 来创建和执行事务: using System; using Microsoft.EntityFrameworkCore; using System.Tran…...

【Python】python天气数据抓取与数据分析(源码+论文)【独一无二】
👉博__主👈:米码收割机 👉技__能👈:C/Python语言 👉公众号👈:测试开发自动化【获取源码商业合作】 👉荣__誉👈:阿里云博客专家博主、5…...

MPPT工作流程及算法和硬件的选择
MPPT算法选择 目前,MPPT算法有开路电压比率(离线)、短路电流比率(离线)、观察调节(在线)、极限追踪控制法(在线)。 在光伏控制系统中,因为日照、温度等条件的变化,光伏电池的输出功率也是在不断变化的,为保证使得光伏电池的输出功…...

C#,《小白学程序》第十九课:随机数(Random)第六,随机生成任意长度的大数(BigInteger)
1 文本格式 using System; using System.Linq; using System.Text; using System.Collections.Generic; /// <summary> /// 大数的(加减乘除)四则运算、阶乘运算 /// 乘法计算包括小学生算法、Karatsuba和Toom-Cook3算法 /// 除法运算为 Truffer…...

每日一练【移动零】
一、题目描述 283. 移动零 - 力扣(LeetCode) 给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。 请注意 ,必须在不复制数组的情况下原地对数组进行操作。 二、题目解析 可以…...

QT修改windowTitle的名字以及图片
1.修改名字:点击ui的QMainWindow,然后找到windowTitle的选项修改即可 2.修改windowTitle的图片,依旧是找到windowIcon,选择资源,这个资源可以是你放到qrc里面的图片也可以是外置的图片 3.然后运行就可以看到效果了...

C语言-指针讲解(3)
文章目录 1.字符指针变量1.1 字符指针变量类型是什么1.2字符指针变量的两种使用方法:1.3字符指针笔试题讲解1.3.1 代码解剖 2.数组指针变量2.1 什么是数组指针2.2 数组指针变量是什么?2.2.3 数组指针变量的举例 2.3数组指针和指针数组的区别是什么&#…...

慢 SQL 分析及优化
目录 分析慢 SQL SQL 优化 单表优化 多表优化 慢 SQL:指 MySQL 中执行比较慢的 SQL排查慢 SQL 最常用的方法:通过慢查询日志来查找慢 SQL MySQL 的慢查询日志是 MySQL 提供的一种日志记录,它用来记录在 MySQL 中响应时间超过阈值的语句&…...
PTA:计算m到n之间所有素数的和
题目 计算m到n之间所有素数的和,其中 2 < m <n <100 输入格式: 请在这里写输入格式。例如:输入两个正整数 输出格式: 请在这里描述输出格式。例如:输出两个正整数之间的素数和。 样例 输入样例: 在这里给出一组输入。例如&…...
Golang实现YOLO:高性能目标检测算法
引言 目标检测是计算机视觉领域的重要任务,它不仅可以识别图像中的物体,还可以标记出物体的位置和边界框。YOLO(You Only Look Once)是一种先进的目标检测算法,以其高精度和实时性而闻名。本文将介绍如何使用Golang实…...
文档 + 模型
文档 模型 0: 基于MATLAB的soc锂电池系统设计 1: 电力系统继电保护仿真设计-毕业论文 2: 继电保护仿真-三段式电流保护的方案设计及分析-相间短路 3: 直流电机双闭环控制系统 转速电流双闭环调速 4: matlab电力系统继电保护仿真 三段电流保护仿真-双侧电源系统 5: OFDM-MIMO课…...

计算机毕业设计php+bootstrap小区物业管理系统
意义:随着我国经济的发展和人们生活水平的提高,住宅小区已经成为人们居住的主流,人们生活质量提高的同时,对小区物业管理的要求也越来越高,诸如对小区的维修维护,甚至对各项投诉都要求小区管理者做得好&…...

Osg线程模型(选择不当,会引发崩溃)
来自《最长的一帧》...

2161根据数字划分数组
给你一个下标从 0 开始的整数数组 nums 和一个整数 pivot 。请你将 nums 重新排列,使得以下条件均成立: 所有小于 pivot 的元素都出现在所有大于 pivot 的元素 之前 。所有等于 pivot 的元素都出现在小于和大于 pivot 的元素 中间 。小于 pivot 的元素之…...

Oracle Linux 9.3 发布
导读Oracle Linux 9 系列发布了第 3 个版本更新,支持 64 位 Intel 和 AMD (x86_64) 以及 64 位 Arm (aarch64) 平台。与所有的 Oracle Linux 版本一样,此版本与相应 RHEL 版本 100% 应用二进制兼容。 对于 x86_64 和 aarch64 架构,Oracle Li…...
XML Schema中的simpleContent 元素
XML Schema中的simpleContent 元素出现在complexType 内部,是对complexType 的一种扩展、或者限制。 simpleContent 元素在complexType元素内部最多只能出现1次。 simpleContent元素下面必须包含1个restriction或者extension元素。 例如,下面的Schema片…...

业务系统对接大模型的基础方案:架构设计与关键步骤
业务系统对接大模型:架构设计与关键步骤 在当今数字化转型的浪潮中,大语言模型(LLM)已成为企业提升业务效率和创新能力的关键技术之一。将大模型集成到业务系统中,不仅可以优化用户体验,还能为业务决策提供…...

使用VSCode开发Django指南
使用VSCode开发Django指南 一、概述 Django 是一个高级 Python 框架,专为快速、安全和可扩展的 Web 开发而设计。Django 包含对 URL 路由、页面模板和数据处理的丰富支持。 本文将创建一个简单的 Django 应用,其中包含三个使用通用基本模板的页面。在此…...
论文解读:交大港大上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(二)
HoST框架核心实现方法详解 - 论文深度解读(第二部分) 《Learning Humanoid Standing-up Control across Diverse Postures》 系列文章: 论文深度解读 + 算法与代码分析(二) 作者机构: 上海AI Lab, 上海交通大学, 香港大学, 浙江大学, 香港中文大学 论文主题: 人形机器人…...

docker详细操作--未完待续
docker介绍 docker官网: Docker:加速容器应用程序开发 harbor官网:Harbor - Harbor 中文 使用docker加速器: Docker镜像极速下载服务 - 毫秒镜像 是什么 Docker 是一种开源的容器化平台,用于将应用程序及其依赖项(如库、运行时环…...
STM32+rt-thread判断是否联网
一、根据NETDEV_FLAG_INTERNET_UP位判断 static bool is_conncected(void) {struct netdev *dev RT_NULL;dev netdev_get_first_by_flags(NETDEV_FLAG_INTERNET_UP);if (dev RT_NULL){printf("wait netdev internet up...");return false;}else{printf("loc…...

【JVM】- 内存结构
引言 JVM:Java Virtual Machine 定义:Java虚拟机,Java二进制字节码的运行环境好处: 一次编写,到处运行自动内存管理,垃圾回收的功能数组下标越界检查(会抛异常,不会覆盖到其他代码…...

Opencv中的addweighted函数
一.addweighted函数作用 addweighted()是OpenCV库中用于图像处理的函数,主要功能是将两个输入图像(尺寸和类型相同)按照指定的权重进行加权叠加(图像融合),并添加一个标量值&#x…...
uniapp中使用aixos 报错
问题: 在uniapp中使用aixos,运行后报如下错误: AxiosError: There is no suitable adapter to dispatch the request since : - adapter xhr is not supported by the environment - adapter http is not available in the build 解决方案&…...
06 Deep learning神经网络编程基础 激活函数 --吴恩达
深度学习激活函数详解 一、核心作用 引入非线性:使神经网络可学习复杂模式控制输出范围:如Sigmoid将输出限制在(0,1)梯度传递:影响反向传播的稳定性二、常见类型及数学表达 Sigmoid σ ( x ) = 1 1 +...
MySQL账号权限管理指南:安全创建账户与精细授权技巧
在MySQL数据库管理中,合理创建用户账号并分配精确权限是保障数据安全的核心环节。直接使用root账号进行所有操作不仅危险且难以审计操作行为。今天我们来全面解析MySQL账号创建与权限分配的专业方法。 一、为何需要创建独立账号? 最小权限原则…...