【JAVA】两轮充电桩设计模式实践分享

news2024/11/28 11:32:36

如果说数据结构和算法是程序员构建高效代码的基石,那么设计模式则是打造高质量代码的“内功心法”。掌握并应用合适的设计模式,有助于管理项目复杂性,简化维护,并加速开发进程。

因此,深入理解并合理运用设计模式,是每个软件开发团队提升项目质量、保障开发效率、促进技术成长的关键步骤。这不仅是程序员个人技术能力的体现,更是软件项目成功的重要保证。在不断变化和技术迭代的开发环境中,掌握设计模式是每个软件开发者不可或缺的技能。

1 项目背景

设计模式(Design Pattern)是软件开发过程中针对常见问题总结出的一套通用解决方案或思路,可广泛应用于不同场景。它的优点在于提高代码的可读性、拓展性和可维护性。然而,使用设计模式时也需警惕过度设计的风险。为避免这一点,应从业务场景出发分析所需解决的具体问题及适用的设计模式,并权衡引入设计模式带来的好处与可能增加的系统复杂度。

在面对两轮充电桩项目(外采项目)这样代码结构复杂、拓展性差、耦合程度高且维护成本高的情况时,设计模式的应用显得尤为重要。我们通过对两轮充电桩业务场景的深入分析和理解,采用恰当的设计模式进行了优化,获得了项目质量及开发效率的双重提升。

质量提升

① 将3个超过5000行的文件拆分为6个不超过500行的文件,提高了代码的可维护性和编译效率;

② 相关模块的代码总量减少了3700多行,简化了代码库;

③ 开发相同模块的平均Bug量从24个下降至10个,显著提升了代码质量和稳定性。

效率提升

① 开发相同模块的预计工时和实际工作量减少了1.5人天,加快了开发速度;

② 模块重构后,冒烟测试工作量从1人天减少至4小时,大幅提高了开发效率。

2 两轮充电桩项目实践

两轮充电桩主要存在以下业务场景:

① 用户登录注册,获取充电桩状态;

② 用户支付并充电,生成充电记录;

③ 根据支付方式和充电方式,实时计费;

④ 充电完成,设备断电,实时结算并退款;

⑤ 管理人员配置充电桩参数,查看订单及报表数据等。

两轮充电桩的整体的业务场景看似简单,实则关联多、流程长:包括多种计费方式、支付方式、开电方式及结算方式,它们相互影响。如果不从整体出发设计,则可能导致项目后期开发、维护及测试成本增加。

2.1 支付方式等优化(工厂模式与策略模式)

工厂模式是一种创建型设计模式,它主要关注对象的创建过程,而非其使用方式。该模式通过提供一个自定义的创建流程来取代直接使用new关键字创建对象,从而实现了对象创建的内部逻辑与使用对象的代码之间的解耦。策略模式是一种非常有用的行为设计模式,它将同一系列的算法定义为一组,在同一组中每个算法可以相互替代,且每个算法都可以独立变化与使用它的调用者。

我们通过采用工厂模式与策略模式实现两轮充电桩项目的支付方式及相关流程的优化,这不仅提升了系统的灵活性,还增强了其扩展性。管理员可以为每个充电桩配置不同的计费方式,满足多样化的收费需求。用户在支付充电费时,能够选择适合自己的计费规则,享受个性化服务。随后,两轮充电桩会根据选定的计费策略进行精确计费,确保费用计算的准确性。最终,系统依据设定的计费方式自动完成结算过程,提高了整体的运营效率和用户体验。

重构前:通过简单的if-else进行判断处理。

① 每增加一种计费逻辑,需要在以上所有地方加if-else判断,违反开闭原则;

② 增加了开发及测试的工作量,需要重复测试已验证过的功能。

public class Demo {   public void pay(ChargeOrder order) {       if (order.getPayWay() == 1) {//先付费模式           if (order.getPayType() == 3) {               //...省略的先付费模式下的计费逻辑           }           if (order.getPayType() == 4) {               //...省略的先付费模式下的计费逻辑           }           //...先付费模式下的其他计费逻辑       } else if (order.getPayWay() == 2) {//后付费模式           //后付费模式下的计费逻辑       }   }}

 

重构后:使用策略模式+工厂模式进行重构。

① 所有支付方式都实现了相同策略接口,面向接口编程,使策略之间可以灵活替换;

② 当新增或减少一种支付方式,主流程不需要做任何改动,可以降低耦合性,提高扩展性;

③ 代码改动量和影响面小,便于测试。

重构后的类图如下:

步骤一:首先定义一个公共的策略接口(XXXStrategy)。这一步是实现基于接口编程思想,它可以让我们灵活地替换策略类。

步骤二:使用简单工厂模式(XXXFactory)来管理具体的策略类。通过封装策略创建的细节及缓存策略,简化策略的创建过程。

步骤三:完善各个策略实现类,以便进行实际业务处理。

步骤四:在使用策略类时,可以根据程序在运行期间传入的不同参数,从工厂类的缓存中获取具体策略类。这样,我们就可以根据需要动态地切换和使用不同的策略。

2.2 操作日志解耦(代理模式)

代理模式属于结构型设计模式,它允许在不修改原始类(被代理类)的代码的情况下,通过引入一个代理类来为原始类增加额外的功能。

日志是对两轮充电桩项目系统运行状态的记录,但不属于核心业务流程,为了解耦这两类逻辑代码,我们引入代理模式。代理模式的实现分为:动态代理与静态代理,当前最常见的为Spring框架中的AOP。

两轮充电桩系统关键节点的日志:

① 管理员添加、编辑用户;

② 用户充值,余额变更;

③ 电卡绑定、修改;

④ 充电参数配置、修改等。

重构前:通过框架注解,代码中直接打印记录日志。

Slf4jPublic class WeChatFreeBalanceRefundService{@Autowired  private ChargeOrderBalanceRecordDAOI chargeOrderBalanceRecordDAOI;
   public void insertRecord(XXX xxx) {log.info("打印的日志", xxx);     }}

重构后:使用Spring AOP记录日志。

2.3 系统远程调用(单例模式)

单例模式是一种常用的设计模式,它确保一个类只有一个实例,并且提供一个全局访问点来获取该实例。

两轮充电桩的支付过程中需要调用如支付宝、微信等外部接口,为此,我们采用一个httpClient单例来有效管理HTTP连接,确保支付请求的高效和稳定。

重构前:通过单例模式自行设计httpClient,但代码复杂度高。一个单例类具有800行代码,使用复杂。两轮充电桩项目中共引用了46个单例类,导致冗余代码过多。

public class HttpRequestPool {// 创建httpclient连接池  private PoolingHttpClientConnectionManager httpClientConnectionManager = null;  private static final HttpRequestPool httpRequestPool = new HttpRequestPool();  //单例模式  public static HttpRequestPool getInstance(){     return httpRequestPool;  }  private HttpRequestPool(){     //创建httpclient连接池     httpClientConnectionManager = new PoolingHttpClientConnectionManager();     //设置连接池最大数量     httpClientConnectionManager.setMaxTotal(50);     //设置单个路由最大连接数量     httpClientConnectionManager.setDefaultMaxPerRoute(50);  }}

重构后:使用Spring封装的RestTemplate,平均每个引用减少代码8行,同时将单例类废除,使得代码更加简洁和易于维护。

2.4 优先支付方式(责任链/模板模式)

责任链模式是一种行为设计模式,它将多个处理器“组装”成一个链条进行处理。即:一个请求先经过A处理器处理,然后再经过B处理器处理,B处理完成交给C处理器,依次类推。每个处理器负责处理请求中与其相关的内容。模板模式也是一种行为设计模式,它在一个算法中定义一个骨架(算法即业务逻辑),并允许将某些步骤的实现延迟到子类中。这使得子类可以在不改变整体算法结构的前提下,重新定义算法中的某些内容。

在两轮充电桩项目中,用户充电完成后,系统将根据当前订单配置的支付方式进行结算。在这一过程中,我们采用了模板模式,它带来了显著的优势:

① 在父类中定义了抽象方法,把这些方法具体的实现放在子类,便于子类拓展;

② 可以在不改变整体业务逻辑的前提下,重新定义业务逻辑中的某些步骤。

重构前:扣款代码通过嵌套if-else语句来实现。

① 破坏了代码的完整性,每次修改都会影响已有结算代码,不符合开闭原则;

重构后:使用责任链模式,每种渠道扣款作为链中的一环,即使引入了多种支付方式,也只需要添加“一环”,不影响其他“环”。

① 避免了大量的代码改动;

② 不影响已验证过的支付方式;

③ 减少测试工作量。

重构后类图实现如下:

② 每次修改结算逻辑,影响所有结算测试用例,增加测试工作量。

2.5 订单状态与计费状态流转(状态机模式)

状态模式是一种行为设计模式,它通过将事件触发的状态转移和动作执行来拆分到不同的状态类中,以此避免分支判断逻辑。

状态模式由状态(State)、事件(Event)以及动作(Action)这三个部分组成。其中,事件也被称为转移条件(Transition Condition)。

在两轮充电桩项目的订单管理系统中,订单具备自身的状态流程,其计费状态受订单状态控制,且在充电过程中还会受到设备上下线的影响。为了应对这种复杂性,我们采用状态机去映射和管理订单状态的流转。这种方法能有效减少条件语句的使用,使代码更加简洁和易于维护。

3 项目总结

设计模式是解决软件开发中常见问题的有效工具,它能够提升代码的可维护性、可重用性和灵活性,同时降低项目复杂度和潜在风险。然而,设计模式并非万能解决方案,盲目套用可能导致代码过于复杂,适得其反。因此,在应用设计模式时,我们必须根据实际业务需求进行权衡和选择,并遵循最佳实践。只有深入理解其原理,并结合具体问题进行分析,才能灵活运用设计模式,确保项目结构更加完整和合理。


本文作者

宋鹤城:碧桂园服务后端开发高级工程师

指导人:

毛卓:碧桂园服务技术总监

刘刚:碧桂园服务集团架构师

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

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

相关文章

HTTP 请求方法(method)介绍

目录 一、GET 二、POST 三、其他方法 3.1 PUT 3.2 DELETE 3.3 HEAD 3.4 OPTIONS 3.5 TRACE 3.6 CONNECT 3.7 PATCH 3.8 LINK 和 UNLINK(扩展方法) HTTP(超文本传输协议)定义了多种请求方法,如: …

opencv计算机视觉识别图像处理c++项目实战python网课程视频教程

课程名称 《OpenCV计算机视觉识别图像处理C实战课程》 课程亮点 全面覆盖:从基础概念到高级技术,涵盖OpenCV的核心功能。实战导向:通过具体项目实践,帮助学员掌握图像处理的实际应用。双语言支持:虽然重点是C&#x…

使用神卓互联内网穿透开发支付宝支付回调环境(Java版)

支付宝支付功能在电商网站和其他在线服务中扮演着重要角色。对于使用Java作为开发语言的应用来说,集成支付宝支付回调功能是必不可少的一部分。为了能够在本地开发环境中测试这一功能,内网穿透技术可以让我们绕过公网IP的限制。下面将介绍如何利用神卓互…

Unity教程(十三)敌人状态机

Unity开发2D类银河恶魔城游戏学习笔记 Unity教程(零)Unity和VS的使用相关内容 Unity教程(一)开始学习状态机 Unity教程(二)角色移动的实现 Unity教程(三)角色跳跃的实现 Unity教程&…

集成电路学习:什么是I/O输入输出

I/O:输入输出 I/O,全称Input/Output,即输入输出,是信息处理系统(如计算机)与外部世界(可能是人类或另一信息处理系统)之间的通信方式。具体来说,输入是系统接收的信号或数…

shell编程--正则表达式

正则表达式 正则表达式都被置于两个正斜杠之间;如/l[oO]ve/ 示例 匹配数字的脚本,用户输入创建账号的数量 语法: [[ ^[0-9]$ ]] 表示必须输入数字 #!/bin/bashwhile : do read -p "输入数字:" numif [[ $num ~ ^[…

springboot整合logback进行日志管理(下篇)

上一篇介绍了springboot整合logback的一些项目中用到的经验,本片文章继续介绍在项目中实际用到的工程经验 1、设想一下有这样一个场景:我想把我所有service层的日志单独打印到service.log中,那应该怎么做? 有人会说了可以配置一…

高收录高权重的采集站源码——码山侠

前端预览:i5i.net——码山侠点击看看 采集非常简单,不伪原创,也不处理种种外链,也不需要什么配图,而且结构也不复杂。 后台管理: 文章发布手动发布及编辑管理后台 采集以及伪原创类工具后台: …

计算机毕业设计PyHive+Hadoop深圳共享单车预测系统 共享单车数据分析可视化大屏 共享单车爬虫 共享单车数据仓库 机器学习 深度学习 PySpark

毕业设计题目基于 Hadoop 的共享单车布局规划 二、毕业设计背景 公共交通工具的“最后一公里”是城市居民出行采用公共交通出行的主要障碍,也是建设绿色城市、低碳城市过程中面临的主要挑战。 共享单车(自行车)企业通过在校园、地铁站点、公…

光储充微电网能量管理系统控制策略研究及并网分析

引言 近年来,我国日益重视分布式能源和微电网的发展,特别是光伏系统和储能技术的应用,这些已成为确保电力供应可靠性、促进新能源利用和减少污染的关键措施。我国新能源领域的专家对光伏储能控制系统进行了深入研究,强调了其应用…

【Qt】 QComboBox | QSpinBox

文章目录 QComboBox —— 下拉框QComboBox 属性核心方法核心信号QComboBox 使用 QSpinBox —— 微调框QSpinBox 属性核心信号QSpinBox 使用 QComboBox —— 下拉框 QComboBox 属性 QComboBox —— 表示下拉框 currentText ——当前选中的文本 currentindex ——当前选中的条…

STM32 ADC采样详解

Content 0x00 前言0x01 ADC配置0x02 滤波处理 0x00 前言 在单片机开发过程中,常常涉及到ADC的使用,市面上大部分便宜的传感器都是采用的ADC来获取其数据,如MQ-2 烟雾传感器、光敏传感器等等。 此类传感器工作原理为根据所采集到的数据变化…

Axure健康助理小程序原型图70+页,医疗类高保真高交互模板

作品概况 页面数量:共 70 页 源文件格式:限 Axure RP 9/10,非app软件无源码 适用领域:医疗健康、健康助理 作品特色 本作品为健康助理小程序的Axure原型设计图,属于医疗健康项目,设计规范内容清晰&#…

坐牢第三十三天(手搓string)

一.mystring类 #include <iostream> #include <cstring> // 引入cstring以使用memcpy using namespace std; class myString {char *str; // 记录c风格的字符串int size; // 记录字符串的实际长度int my_capacity; // 记录最大容量 public:// 无参构…

LACP链路聚合

链路聚合包含两种模式&#xff1a;手动负载均衡模式和LACP&#xff08;Link AggregationControl Protocol&#xff09;模式。 手工负载分担模式&#xff1a;Eth-Trunk的建立、成员接口的加入由手工配置&#xff0c;没有链路聚合控制协议的参与。该模式下所有活动链路都参与数…

嵌入式软件--51单片机 DAY 2

一、数码管 1.数码管概况 2.设计 &#xff08;1&#xff09;硬件设计 我们可以通过阴极控制显示的位置&#xff0c;通过阳极控制显示的内容。两个数码管共有8个阴极引脚和16和阳极引脚&#xff0c;如果所有引脚都直接接入MCU&#xff0c;会造成MCU引脚的极大浪费。 为了节省…

Java编程基础-类加载机制

文章目录 基本概念类加载器的层次结构类的加载过程双亲委派模型自定义类加载器注意事项 Java 类加载器&#xff08;Class Loader&#xff09;是 Java 运行时环境的一部分&#xff0c;它负责在应用程序运行时加载类和接口的字节码。类加载器对于 Java 的动态特性和安全性有着至关…

Microsoft DirectML 现在支持 Copilot+ PC 和 WebNN

DirectML 是一个底层应用程序接口&#xff0c;使开发人员能够在任何兼容 DirectX 12 的 AMD、英特尔和英伟达&#xff08;NVIDIA&#xff09;GPU 上运行机器学习工作负载 。它首次出现在Windows 10 的 1903 版本中。最近&#xff0c;微软 开始在 DirectML 中支持现代 SoC 中的N…

新能源汽车超级电容和电池能量管理系统的simulink建模与仿真

目录 1.课题概述 2.系统仿真结果 3.核心程序与模型 4.系统原理简介 4.1 电池模型 4.2 电池荷电状态&#xff08;SOC&#xff09;估算 4.3 超级电容器模型 4.4 能量管理 5.完整工程文件 1.课题概述 新能源汽车的能量管理系统&#xff08;Energy Management System, EMS…

Conda离线部署django

要在没有网络连接的环境中使用conda部署Django&#xff0c;你需要预先在有网络连接的机器上创建一个包含所有必要包的环境&#xff0c;并导出该环境的配置文件。然后&#xff0c;你可以将这个配置文件和必要的包传输到目标机器上进行安装。 下面是详细的步骤&#xff1a; 1. …