如果说数据结构和算法是程序员构建高效代码的基石,那么设计模式则是打造高质量代码的“内功心法”。掌握并应用合适的设计模式,有助于管理项目复杂性,简化维护,并加速开发进程。
因此,深入理解并合理运用设计模式,是每个软件开发团队提升项目质量、保障开发效率、促进技术成长的关键步骤。这不仅是程序员个人技术能力的体现,更是软件项目成功的重要保证。在不断变化和技术迭代的开发环境中,掌握设计模式是每个软件开发者不可或缺的技能。
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 项目总结
设计模式是解决软件开发中常见问题的有效工具,它能够提升代码的可维护性、可重用性和灵活性,同时降低项目复杂度和潜在风险。然而,设计模式并非万能解决方案,盲目套用可能导致代码过于复杂,适得其反。因此,在应用设计模式时,我们必须根据实际业务需求进行权衡和选择,并遵循最佳实践。只有深入理解其原理,并结合具体问题进行分析,才能灵活运用设计模式,确保项目结构更加完整和合理。
本文作者
宋鹤城:碧桂园服务后端开发高级工程师
指导人:
毛卓:碧桂园服务技术总监
刘刚:碧桂园服务集团架构师