Spring项目中用了这种解耦模式,老大对我刮目相看

news2024/12/27 0:50:29

前言

不知道大家在项目中有没有遇到过这样的场景,根据传入的类型,调用接口不同的实现类或者说服务,比如根据文件的类型使用 CSV解析器或者JSON解析器,在调用的客户端一般都是用if else去做判断,比如类型等于JSON,我就用JSON解析器,那如果新加一个类型的解析器,是不是调用的客户端还要修改呢?这显然太耦合了,本文就介绍一种方法,服务定位模式Service Locator Pattern来解决,它帮助我们消除紧耦合实现及其依赖性,并提出将服务与其具体类解耦。

一个例子入门

我们通过一个例子来告诉你如何使用Service Locator Pattern。

假设我们有一个从各种来源获取数据的应用程序,我们必须解析不同类型的文件,比如解析CSV文件和JSON文件。

  1. 定义一个类型的枚举

public enum ContentType {
  JSON,
  CSV
}
  1. 定义一个解析的接口

public interface Parser {
  List parse(Reader r);
}
  1. 根据不同的文件类型有不同的实现类

// 解析csv
@Component
public class CSVParser implements Parser { 
  @Override
  public List parse(Reader r) { .. }
}

// 解析json
@Component
public class JSONParser implements Parser {
  @Override
  public List parse(Reader r) { .. }
}
  1. 最后写一个调用的客户端,通过switch case根据不同的类型调用不同的实现

@Service
public class Client {
  private Parser csvParser, jsonParser;
    
  @Autowired
  public Client(Parser csvParser, Parser jsonParser) {
    this.csvParser = csvParser;
    this.jsonParser = jsonParser;
  }
    
  public List getAll(ContentType contentType) {
    ..
    
    switch (contentType) {
      case CSV:
        return csvParser.parse(reader);
      case JSON:
        return jsonParser.parse(reader);
      ..
    }
  }
  ..
}

可能大部分人都是像上面一样的方式实现的,也能正常运行,那深入思考下,存在什么问题吗?

现在假如产品经理提出了一个新需求要支持XML类型的文件,是不是客户端也要修改代码,需要在switch case中添加新的类型,这就导致客户端和不同的解析器紧密耦合。

那么有什么更好的方法呢?

应用Service Locator Pattern

没错,那就是用上我们的服务定位模式Service Locator Pattern。

  1. 让我们定义我们的服务定位器接口ParserFactory, 它有一个接受内容类型参数并返回Parser的方法。

public interface ParserFactory {
  Parser getParser(ContentType contentType);
}
  1. 我们配置ServiceLocatorFactoryBean使用ParserFactory作为服务定位器接口,ParserFactory这个接口不需要写实现类。

@Configuration
public class ParserConfig {
    
  @Bean("parserFactory")
  public FactoryBean serviceLocatorFactoryBean() {
    ServiceLocatorFactoryBean factoryBean = new ServiceLocatorFactoryBean();
    // 设置服务定位接口   
    factoryBean.setServiceLocatorInterface(ParserFactory.class);
    return factoryBean;
  }

}
  1. 设置解析器Bean的名称为类型名称,方便服务定位

// 设置bean的名称和类型一致
@Component("CSV")
public class CSVParser implements Parser { .. }
@Component("JSON")
public class JSONParser implements Parser { .. }
@Component("XML")
public class XMLParser implements Parser { .. }
  1. 修改枚举, 添加XML

public enum ContentType {
  JSON,
  CSV,
  XML
}
  1. 最后用客户端调用,直接根据类型调用对应的解析器,没有了switch case

@Service
public class Client {
  private ParserFactory parserFactory;
  @Autowired
  public Client(ParserFactory parserFactory) {
    this.parserFactory = parserFactory;
  }
  public List getAll(ContentType contentType) {
    ..
    // 关键点,直接根据类型获取
    return parserFactory
        .getParser(contentType)  
        .parse(reader);
  }
  ..
}

嘿嘿,我们已经成功地实现了我们的目标。现在再加新的类型,我们只要扩展添加新的解析器就行,再也不用修改客户端了,满足开闭原则。

如果你觉得Bean的名称直接使用类型怪怪的,这边可以建议你按照下面的方式来。

public enum ContentType {
  JSON(TypeConstants.JSON_PARSER),
  CSV(TypeConstants.CSV_PARSER),
  XML(TypeConstants.XML_PARSER);
  private final String parserName;
  ContentType(String parserName) {
    this.parserName = parserName;
  }
  
  @Override
  public String toString() {
    return this.parserName;
  }
  public interface TypeConstants {
    
    String CSV_PARSER = "csvParser";
    String JSON_PARSER = "jsonParser";
    String XML_PARSER = "xmlParser"; 
  }
}

@Component(TypeConstants.CSV_PARSER)
public class CSVParser implements Parser { .. }
@Component(TypeConstants.JSON_PARSER)
public class JSONParser implements Parser { .. }
@Component(TypeConstants.XML_PARSER)
public class XMLParser implements Parser { .. }

剖析Service Locator Pattern

通过前面的例子,想必大家基本知道服务定位器模式如何使用了吧,现在我们深入剖析下。

服务定位器模式消除了客户端对具体实现的依赖。以下引自 Martin Fowler 的文章总结了核心思想: “服务定位器背后的基本思想是拥有一个知道如何获取应用程序可能需要的所有服务的对象。因此,此应用程序的服务定位器将有一个在需要时返回“服务”的方法。”

Spring 的ServiceLocatorFactoryBean实现了 FactoryBean接口,创建了Service Factory服务工厂Bean。

总结

我们通过使用服务定位器模式实现了一种扩展 Spring 控制反转的绝妙方法。它帮助我们解决了依赖注入未提供最佳解决方案的用例。也就是说,依赖注入仍然是首选,并且在大多数情况下不应使用服务定位器来替代依赖注入。

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

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

相关文章

Python Web开发:用Tornado框架制作一个简易的网站

前言 大家早好、午好、晚好吖 ❤ ~ 今天我们要用Python做Web开发,做一个简单的【表白墙】网站。 众所周知表白墙的功能普遍更多的是发布找人,失物招领,还是一个大家可以跟自己喜欢的人公开表白的平台 Tornado框架简单介绍 在Python当中&am…

SpringMVC笔记【JavaEE】

SpringMVC 1. SpringMVC概念 Spring MVC 是一个Web 框架。Spring MVC 是基于Servlet API构建的。 MVC是 Model(模型) View(视图) Controller(控制器) 的缩写,它是一种设计模式。 视图分为两种&…

2022年山东省中职组“网络安全”赛项比赛任务书正式赛题

2022年山东省中职组“网络安全”赛项 比赛任务书 一、竞赛时间 总计:360分钟 竞赛阶段竞赛阶段 任务阶段 竞赛任务 竞赛时间 分值 A模块 A-1 登录安全加固 180分钟 200分 A-2 Nginx安全策略 A-3 日志监控 A-4 中间件服务加固 A-5 本地安全策略…

html的表单标签(form)

目录标题1、表单标签主要有三大类:2、表单标签中常见的属性3、例子代码及结果4、注意:5、表单中特殊的属性表单标签可以用来数据交互,而前面学的六个标签只能发送不能接收。 表单标签的作用就是数据交互1、表单标签主要有三大类: …

搭建本地私有仓库

目录 一、搭建本地私有仓库 1、首先下载registry镜像 2、在daemon.json文件中添加私有镜像仓库地址 3、运行restart容器 4、为镜像打标签 5、上传到私有仓库 6、列出私有仓库的所有镜像 7、列出私有仓库的centos镜像有哪些tag 8、删除原有的centos的镜像,再测试…

双目测距------双目相机V1.0,将双目相机采集到任意一点的深度数据进行串口传输(带源码)

Depth2Uart 双目测距------双目相机V1.0,将双目相机采集到任意一点的深度数据进行串口传输 一、项目说明/Overview 所实现的功能:基于Intel Realsense官方提供的SDK,双目深度相机能获取到相机任何一个像素点距离前方障碍物的距离&#xff0…

电子学会2022年12月青少年软件编程(图形化)等级考试试卷(三级)答案解析

目录 一、单选题(共25题,共50分) 二、判断题(共10题,共20分) 三、编程题(共3题,共30分) 青少年软件编程(图形化)等级考试试卷(三级) 一、单选题(共25题,共50分) 1. 默认小猫角色…

FlinkCEP - Flink的复杂事件处理

版本说明 本文中以Flink 1.16.1 版本讲解说明 Note:Flink1.16.1版本相较于之前版本增强的within函数, 支持模式序列中相邻事件间的超时定义,以前版本只支持模式序列中第一个事件到最后一个事件之间的最大时间间隔。 快速开始 基于Kafka connecter 流…

C语言速成(有基础)

linux下的 是一种通用的、面向过程式的计算机编程语言 #include <stdio.h> //#include 预处理命令&#xff0c;用来引用头文件&#xff0c; stdio.h 头文件 int main() //开始 {/* 一个注释 */printf("Hello, World! \n");return 0; …

最强大的人工智能chatGPT不会还有人没用过吧,再不用就out了

&#x1f517; 运行环境&#xff1a;chatGPT &#x1f6a9; 撰写作者&#xff1a;左手の明天 &#x1f947; 精选专栏&#xff1a;《python》 &#x1f525; 推荐专栏&#xff1a;《算法研究》 #### 防伪水印——左手の明天 #### &#x1f497; 大家好&#x1f917;&#x1f9…

JUC并发编程 Ⅱ -- 共享模型之管程(上)

文章目录共享带来的问题临界区 Critical Section竞态条件 Race Conditionsynchronized 解决方案synchronized语法解决方案思考面向对象改进方法上的 synchronized线程八锁变量的线程安全分析成员变量和静态变量是否线程安全&#xff1f;局部变量是否线程安全&#xff1f;局部变…

搜索插入位置-力扣35-java

一、题目描述给定一个排序数组和一个目标值&#xff0c;在数组中找到目标值&#xff0c;并返回其索引。如果目标值不存在于数组中&#xff0c;返回它将会被按顺序插入的位置。请必须使用时间复杂度为 O(log n) 的算法。示例 1:输入: nums [1,3,5,6], target 5输出: 2示例 2:输…

制作自己的ChatGPT

众所周知&#xff0c;ChatGPT 目前能够取得令人印象深刻的壮举。 很可能许多人都有在他们自己的项目中使用该技术的想法。 不过需要注意的是&#xff0c;ChatGPT 目前并没有官方的 API。 使用非官方 API 可能会导致困难。 目前需要手动获取access token和cloudflare token才能…

基于Java+SpringBoot+Vue前后端分离学生宿舍管理系统设计与实现

博主介绍&#xff1a;✌全网粉丝3W&#xff0c;全栈开发工程师&#xff0c;从事多年软件开发&#xff0c;在大厂呆过。持有软件中级、六级等证书。可提供微服务项目搭建、毕业项目实战、项目定制✌ 博主作品&#xff1a;《微服务实战》专栏是本人的实战经验总结&#xff0c;《S…

智慧水务未来技术发展方向预测探讨

随着科技的不断发展和城市化的加速&#xff0c;智慧水务作为一种新的水务模式&#xff0c;逐渐受到广泛关注。未来&#xff0c;智慧水务将会面临更多的技术挑战和商机。本博客将对智慧水务的未来技术发展方向进行预测&#xff0c;以探讨智慧水务未来可能的技术重点。 1. 人工…

MySQL数据库11——子查询语句

嵌入一个SELECT的语句称为子查询语句。虽然内连接也可以代替子查询&#xff0c;但是内连接效率不高。而且我个人觉得子查询更好理解。 单值子查询 果子查询返回单值&#xff0c;则可以使用关系运算符&#xff0c;例如&#xff0c;等于&#xff08;&#xff09;、不等于&#x…

【C语言】“指针的运算”、“指针与数组”

文章目录一、指针运算1.指针 - 整数2.指针-指针3.指针关系运算二、指针与数组三、二级指针四、指针数组完结一、指针运算 指针可以进行整数&#xff0c;指针-指针&#xff0c;还有关系运算&#xff0c;其他的运算会被编译器阻止。 1.指针 - 整数 对指针进行的时候一定要注意不…

【SSM】篇一:初试Spring--Ioc与Bean

文章目录1、Spring2、SpringFramework系统架构3、Bean--Bean的配置Bean的实例化Bean的生命周期1、Spring Spring地址&#xff1a;https://spring.io Spring技术的优点&#xff1a; Spring家族&#xff08;Spring全家桶&#xff09;&#xff1a; 2、SpringFramework系统架构 S…

MySQL数据库10——多表连接查询

数据如果在多个表里面&#xff0c;需要进行连接查询。 一般在pandas里面merge合并会用到一个索引&#xff0c;按这个索引的规则进行合并叫做有规则的等值连接。若不按规则连接&#xff0c;遍历两两组合的所有可能性&#xff0c;叫做笛卡尔积。 笛卡尔积连接 通常人们都会设置…

R语言学习笔记

1.R语言介绍 2.R语言安装 官网&#xff1a;https://www.r-project.org/ CARN → 选择China中任意镜像站点 → Download R for Windows → base&#xff08;二进制版本R基础软件&#xff09;→ Download R-4.2.2 for Windows (76 megabytes, 64 bit) 3.Rstudio安装 https://po…