Java设计模式实战:从If-Else到策略+工厂方法的演变

引言

可能很多开发者,虽然理解了设计模式的概念,但在实际工作中应用却是另一回事。本篇文章旨在用一个具体的案例来展示如何将设计模式应用到工作的编程问题中。正所谓:“纸上得来终觉浅,绝知此事要躬行。”理论的学习固然重要,但只有通过实战,我们才能真正掌握并灵活运用。

我们将以一个物流系统为例,展示如何从大量if-else语句的初始代码,一步步演进到应用工厂模式、策略模式的优雅设计。通过这个过程,你将看到设计模式如何帮助我们改进代码结构,提高其可维护性和可扩展性。

如果你觉得这篇文章对你有帮助,请记得点赞和关注,支持一下!

目录

  1. 问题场景分析
    • 初始代码实现
    • 存在的问题点分析
  2. 引入策略模式
  3. 引入简单工厂模式
  4. 引入工厂方法模式
  5. 总结

问题场景分析

在这个物流系统中,根据包裹的不同特性(如目的地、大小、重量或优先级)来选择合适的物流供应商是一项常见任务。这个过程涉及到多个决策点,每个决策点都可能依赖于包裹的不同属性。例如,国际快递和本地配送就需要不同的处理逻辑。

随着业务的发展,新的供应商加入,特殊的配送需求增加,原本简单的决策逻辑迅速膨胀,变得越来越复杂。这不仅使得代码难以维护,也增加了引入错误的风险。

初始代码实现

初始的实现可能直接使用if-else语句来处理这些逻辑。虽然这种方法直观,但随着逻辑的增加,代码会变得越来越长,越来越难以理解和维护

public class LogisticsService {
    public void processPackage(Package pkg) {
        if (pkg.getDestination().equals("国际")) {
            // 处理国际物流
            processInternalPackage(pkg);
        } else if (pkg.getSize() > 30) {
            // 处理大件物流
            processBigSizePackage(pkg);
        } else if (pkg.isExpress()) {
            // 处理快递服务
            processExpressPackage(pkg);
        } else {
			// ....
        }
        // 可能还有更多的if-else逻辑...
    }
}
存在的问题点分析

这种基于if-else的实现方式有几个主要问题:

违反开闭原则:对于新的供应商或规则的添加,需要修改现有代码,而不是扩展
低可维护性:随着条件逻辑的增加,代码变得越来越复杂,越来越难以维护
低可测试性:复杂的条件逻辑使得编写和维护测试变得困难

引入策略模式

策略模式概述

策略模式是一种行为设计模式,它定义了算法族,分别封装起来,让它们之间可以互相替换。这种模式让算法的变化独立于使用算法的客户端。在我们的物流系统案例中,策略模式允许我们根据包裹的不同特性动态选择合适的物流处理策略
策略模式的类图
策略模式类图

应用策略模式重构

由于我们的物流系统需要根据包裹的目的地、大小和是否快递等因素选择不同的物流供应商。我们可以为每种情况定义一个策略,并结合使用Spring的依赖注入来管理这些策略

①定义策略接口

首先,我们定义一个策略接口,表示一个物流处理策略:

public interface LogisticsStrategy {
	// 根据包裹重量,匹配策略
    boolean appliesTo(Package pkg);
	// 处理包裹
    void processPackage(Package pkg);
}

②实现具体策略

为每种物流情况定义具体的策略类:

@Service
public class InternationalLogisticsStrategy implements LogisticsStrategy {
    @Override
    public boolean appliesTo(Package pkg) {
        return "国际".equals(pkg.getDestination());
    }

    @Override
    public void processPackage(Package pkg) {
        // 实现国际物流处理逻辑
    }
}

@Component
public class PkgSizeLogisticsStrategy implements LogisticsStrategy {

 	@Override
    public boolean appliesTo(Package pkg) {
        return pkg.size() > 30;
    }
    
    @Override
    public void processPackage(Package pkg) {
        // 实现大件物流处理逻辑
    }
}

@Component
public class DefaultLogisticsStrategy implements LogisticsStrategy {

 	@Override
    public boolean appliesTo(Package pkg) {
        //....
    }
    
    @Override
    public void processPackage(Package pkg) {
        // ...
    }
}
// 其他策略类...
③使用策略
@Service
public class LogisticsService {
	private final List<LogisticsStrategy> strategies;
	
	public void processPackage(Package pkg) {
	   LogisticsStrategy strategy = strategies.stream()
	         .filter(s -> s.appliesTo(pkg))
	         .findFirst()
	         .orElse(new DefaultLogisticsStrategy());
	         
	   strategy.processPackage(pkg);
	}
}

在这个重构后的版本中,LogisticsService通过构造函数注入的方式获取所有策略实现,从而避免了硬编码的if-else逻辑
现在我们来看下整体的类图
引入策略后的类图
在这个类图中:

  • LogisticsService类包含一个 List 类型的字段,用于存储不同的物流策略
  • LogisticsStrategy是一个接口,定义了appliesToprocessPackage方法,用于判断策略是否适用于特定的包裹,并处理包裹。
  • InternationalLogisticsStrategyDefaultLogisticsStrategy是实现了LogisticsStrategy接口的具体策略类

存在的缺陷分析

  • LogisticsService除了负责处理包裹,还要负责选择策略,显然违背了单一职责
  • LogisticsService依赖于具体的策略实现,没有依赖于抽象,显然违背了依赖倒置原则

因此考虑是否可以引入工厂模式?让选择策略的逻辑由工厂实现,解决单一职责问题。解耦LogisticsService与具体的策略实现,让LogisticsService只依赖于抽象的工厂,解决依赖倒置原则问题,显然是可行的,接下来我们走进引入工厂模式的篇章

引入简单工厂模式

简单工厂模式概述

工厂模式是一种创建型设计模式,用于提供一个创建对象的接口,从而将对象的实例化逻辑从使用对象的代码中分离出来。在我们的物流系统案例中,工厂模式可以用来灵活地创建和管理不同的物流策略对象,看下简单工厂的类图
简单工厂uml类图
在这个类图中:

  • SimpleFactory 是一个类,提供了一个 createProduct 方法,用于根据类型创建并返回 Product 类的对象
  • Product 是一个接口或抽象类,定义了产品的接口
  • ConcreteProductAConcreteProductBProduct 的具体实现

应用简单工厂模式重构

为了优化策略模式的实现,我们引入工厂模式来负责策略对象的创建和管理,从而简化LogisticsService类的职责

定义简单策略工厂

该类在Spring容器启动时自动注册所有策略实例,用与生产具体的物流策略

@Component
public class SimpleLogisticsStrategyFactory {
    private final List<LogisticsStrategy> strategies;

    @Override
    public LogisticsStrategy createStrategy(Package pkg) {
        return strategies.stream()
                         .filter(strategy -> strategy.appliesTo(pkg))
                         .findFirst()
                         .orElse(new DefaultLogisticsStrategy());
    }
}
修改LogisticsService

最后,LogisticsService类通过工厂类获取策略对象,而不是直接与具体策略类交互:

@Service
public class LogisticsService {
    private final SimpleLogisticsStrategyFactory strategyFactory;

    public void processPackage(Package pkg) {
        LogisticsStrategy strategy = strategyFactory.createStrategy(pkg);
        strategy.processPackage(pkg);
    }
}

现在似乎解决了所有问题,引入了SimpleLogisticsStrategyFactory来负责创建具体的物流策略,解偶LogisticsService与策略对象,看起来很完美,是这样吗?
问题点分析:

  • LogisticsService依赖了具体的工厂实现类,没有依赖于抽象,显然这违反了依赖倒置原则
  • 假如随着业务的发展,需要为特定类型的客户提供定制化的物流策略,按照当前的设计,只能再新建一个CustomLogisticsStrategyFactory,根据客户来选择不同的物流策略,但是这样的话LogisticsService这个类的代码就得修改,显然这违反了开闭原则

引入工厂方法模式

工厂方法模式概述

工厂方法模式是一种创建型设计模式,它通过定义一个创建对象的接口并让子类决定具体要实例化的类,从而在不指定具体类的情况下创建对象,增加了代码的灵活性和扩展性
工厂方法uml类图
在这个类图中:

  • Creator 是一个抽象类或接口,定义了一个工厂方法factoryMethod(),用于创建Product类的对象
  • ConcreteCreator是Creator的一个具体实现,实现了factoryMethod() 方法,返回ConcreteProduct的实例
  • Product是一个接口或抽象类,定义了产品的接口。ConcreteProduct是Product的具体实现
实现工厂方法模式

为了应用工厂方法模式,我们定义一个工厂接口,负责根据包裹的特性创建合适的物流策略。

public interface LogisticsStrategyFactory {
    LogisticsStrategy createStrategy(Package pkg);
}

public class InternationalLogisticsFactory implements LogisticsStrategyFactory {
    @Override
    public LogisticsStrategy createStrategy(Package pkg) {
        return new InternationalLogisticsStrategy();
    }
}

public class PkgSizeLogisticsStrategyFactory implements LogisticsStrategyFactory {
    @Override
    public PkgSizeLogisticsStrategyFactory createStrategy(Package pkg) {
        return new PkgSizeLogisticsStrategy();
    }
}

public class LogisticsService {
	// 	依赖于接口
    private LogisticsStrategyFactory factory;

    public void processPackage(Package pkg) {
		// 根据工厂创建策略
        LogisticsStrategy strategy = factory.createStrategy(pkg);
        strategy.processPackage(pkg);
    }
}

通过工厂方法模式进一步提高了系统的灵活性和可扩展性。每个工厂类负责创建特定类型的物流策略,而服务类则通过工厂接口与具体的策略创建逻辑解耦。工厂负责创建具体的策略,LogisticsService只关注于处理包裹的逻辑,与策略实现类解耦

总结

通过引入策略模式、工厂模式(包括简单工厂和工厂方法模式),我们成功地将一个复杂且难以维护的物流系统重构成了一个灵活、可扩展且易于维护的系统。这个过程展示了设计模式在实际编程中的强大作用,特别是在面对复杂系统时,它们能够帮助我们更好地组织和优化代码

希望这篇文章能够帮助你更好地理解和应用设计模式。切记,理论知识的学习是基础,但只有通过实际的应用和实践,才能真正掌握。希望每个开发者都能在日常的编程工作中尝试和应用这些设计模式。如果你觉得这篇文章对你有帮助,请给我点赞和关注。感谢阅读!

  • 程序员三毛

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

若依generator模块解读,Java小白入门(七)

模块基本内容 若依的核心模块基本除了安全那一块&#xff0c;现在还是剩下一个比较重要的章节&#xff0c;是ruoyi-generator,就是代码根据模板来生成&#xff0c;这一块很有必要进行深入了解&#xff0c;本章节我们弄清楚基本框架&#xff0c;以后&#xff0c;我们会在这一块…

回顾 2023,展望 2024

by zhengkai.blog.csdn.net 项目与心得 今年最大的项目和心得&#xff0c;非GCP莫属&#xff0c;作为全球顶尖的云平台&#xff0c; GCP有他的优势&#xff0c;也有很多难用的地方。但是作为当时的一个strategic solution&#xff0c;我们的印度本地化项目必须使用GCP&#xf…

丰田「退股」电装,传统汽车供应链体系走到十字路口

就在中国市场热衷于车企与零部件厂商的合纵连横之际&#xff0c;本周&#xff0c;传统汽车巨头丰田公司宣布&#xff0c;将出售部分电装&#xff08;Denso&#xff09;公司股份&#xff0c;资金用于投入电动化、智能驾驶等新技术研发。 按照计划&#xff0c;丰田汽车拟出售超过…

OpenCV-11颜色通道的分离与合并

本次我们使用两个比较重要的API split&#xff08;mat&#xff09;将图像的通道进行分割。 merge&#xff08;(ch1&#xff0c;ch2&#xff0c;ch3)&#xff09;将多个通道进行融合。 示例代码如下&#xff1a; import cv2 import numpy as npimg np.zeros((480, 640, 3),…

【python 的各种模块】(8) 在python使用matplotlib和wordcloud库来画wordcloud词云图

目录 目标&#xff1a;用python画出&#xff0c;网上流行的wordcloud词云图 1 准备工作 1.1环境准备 1.1.1安装步骤 1.2 资源准备 1.2.1 文本文件内容如下 1.2.2 图片资源 2 代码测试 2.1 第一版代码和效果 2.1.1 代码和效果 2.1.2 一般plt里解决中文乱码问题 2.1…

【网络面试(4)】协议栈和套接字及连接阶段的三次握手原理

1. 协议栈 一直对操作系统系统的内核协议栈理解的比较模糊&#xff0c;借着这一篇博客做一下简单梳理&#xff0c; 我觉得最直白的理解就是&#xff0c;内核协议栈就是操作系统中的一个网络控制软件&#xff0c;就是一段程序代码&#xff0c;它负责和网卡驱动程序交互&#xff…

Python玫瑰花完整代码

文章目录 环境需求完整代码普通玫瑰花三维玫瑰花多彩玫瑰花环境需求 python3.11.4PyCharm Community Edition 2023.2.5pyinstaller6.2.0(可选,这个库用于打包,使程序没有python环境也可以运行,如果想发给好朋友的话需要这个库哦~)【注】 python环境搭建请见:https://want…

文献阅读:LoRA: Low-Rank Adaptation of Large Language Models

文献阅读&#xff1a;LoRA: Low-Rank Adaptation of Large Language Models 1. 文章简介2. 方法介绍3. 实验 & 结论 1. 基础实验 1. Bert系列模型2. GPT系列模型 2. 消解实验 1. 作用矩阵考察2. 中间维度考察3. 扰动程度分析 4. 总结 & 思考 文献链接&#xff1a;htt…

骑砍战团MOD开发(29)-module_scenes.py游戏场景

骑砍1战团mod开发-场景制作方法_哔哩哔哩_bilibilihttps://www.bilibili.com/video/BV1Cw411N7G4/ 一.骑砍游戏场景 骑砍战团中进入城堡,乡村,战斗地图都被定义为场景,由module_scenes.py进行管理。 scene(游戏场景) 天空盒(Skyboxes.py) 地形(terrain code) 场景物(scene_…

WorkPlus私有化即时通讯的标杆,助力企业实现信息管控与保障

在信息时代&#xff0c;保护企业的信息安全至关重要。而私有化即时通讯成为了企业提升信息安全的重要手段。作为私有化即时通讯的领先选择&#xff0c;WorkPlus以其卓越的性能和领先的技术&#xff0c;为企业提供了安全可靠的通信解决方案。 私有化即时通讯是企业保护信息安全的…

新手快速上手掌握基础排序<二>快速排序快速入门

目录 引言 一&#xff1a;快速排序qsort的简介 1.qsort是一个库函数 2.库函数的查询了解方法 3.qsort的具体使用方法 4.qsort函数使用的一些注意点 5.qsort函数的特点 6.代码实现 (1)整数数组的快速排序 &#xff08;2&#xff09;结构体的快速排序&#xff08;学…

在Go中使用Goroutines和Channels发送电子邮件

学习如何使用Goroutines和Channels在Go中发送电子邮件 在现代软件开发的世界中&#xff0c;通信是一个关键元素。发送电子邮件是各种目的的常见实践&#xff0c;例如用户通知、报告等。Go是一种静态类型和编译语言&#xff0c;为处理此类任务提供了高效和并发的方式。在本文中&…

论文阅读: AAAI 2022行人重识别方向论文-PFD_Net

本篇博客用于记录一篇行人重识别方向的论文所提出的优化方法《Pose-Guided Feature Disentangling for Occluded Person Re-identification Based on Transformer》&#xff0c;论文中提出的PDF_Net模型的backbone是采用《TransReID: Transformer-based Object Re-Identificati…

numpy数组04-数组的轴和读取数据

一、数组的轴 在numpy中数组的轴可以理解为方向&#xff0c;使用0&#xff0c;1&#xff0c;2...数字表示。 对于一个一维数组&#xff0c;只有一个0轴&#xff0c;对于2维数组&#xff08;如shape&#xff08;2&#xff0c;2&#xff09;&#xff09;&#xff0c;有0轴和1轴…

计算机操作系统(OS)——P3内存管理

1、内存的基础知识 学习目标&#xff1a; 什么是内存&#xff1f;有何作用&#xff1f; 内存可存放数据。程序执行前__需要先放内存中才能被CPU处理__——缓和CPU与硬盘之间的速度矛盾。 【思考】在多道程序程序下&#xff0c;系统会有多个进程并发执行&#xff0c;也就是说…

「GPT」G、P、T分别是啥意思?

G意为Generative &#xff1a;生成式 比如&#xff0c;生成式的分类器&#xff08;模型&#xff09;包括---- generative classifiers: naive Bayes classifier and linear discriminant analysis 与之对应的为判别式----- discriminative model: logistic regression P意为…

Java项目:102SSM汽车租赁系统

博主主页&#xff1a;Java旅途 简介&#xff1a;分享计算机知识、学习路线、系统源码及教程 文末获取源码 一、项目介绍 汽车租赁系统基于SpringSpringMVCMybatis开发&#xff0c;系统使用shiro框架做权限安全控制&#xff0c;超级管理员登录系统后可根据自己的实际需求配角色…

2024年原创深度学习算法项目分享

原创深度学习算法项目分享&#xff0c;包括以下领域&#xff1a; 图像视频、文本分析、知识图谱、推荐系统、问答系统、强化学习、机器学习、多模态、系统界面、爬虫、增量学习等领域… 有需要的话&#xff0c;评论区私聊

Cookie、Session

一、会话管理 1、什么是会话&#xff1f; 会话是客户端和服务端之间进行多次的请求和响应。 相当于两个人聊天&#xff0c;进行了多次的问答。 对多次问答的管理叫做会话管理&#xff0c;管理的东西是通信状态。 2、什么是状态&#xff1f; 举例&#xff1a; 小明去校园食堂…

QT上位机开发(抽奖软件)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 用抽奖软件抽奖&#xff0c;是一种很常见的抽奖方式。特别是写这篇文章的时候&#xff0c;正好处于2023年12月31日&#xff0c;也是一年中最后一天…