Spring项目中用了这种解耦模式,经理对我刮目相看
前言
不知道大家在项目中有没有遇到过这样的场景,根据传入的类型,调用接口不同的实现类或者说服务,比如根据文件的类型使用 CSV解析器或者JSON解析器,在调用的客户端一般都是用if else
去做判断,比如类型等于JSON,我就用JSON解析器,那如果新加一个类型的解析器,是不是调用的客户端还要修改呢?这显然太耦合了,本文就介绍一种方法,服务定位模式Service Locator Pattern
来解决,它帮助我们消除紧耦合实现及其依赖性,并提出将服务与其具体类解耦。
欢迎关注个人公众号『JAVA旭阳』交流沟通
一个例子入门
我们通过一个例子来告诉你如何使用Service Locator Pattern
。
假设我们有一个从各种来源获取数据的应用程序,我们必须解析不同类型的文件,比如解析CSV文件和JSON文件。
- 定义一个类型的枚举
public enum ContentType {
JSON,
CSV
}
- 定义一个解析的接口
public interface Parser {
List parse(Reader r);
}
- 根据不同的文件类型有不同的实现类
// 解析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) { .. }
}
- 最后写一个调用的客户端,通过
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
。
- 让我们定义我们的服务定位器接口
ParserFactory
, 它有一个接受内容类型参数并返回Parser
的方法。
public interface ParserFactory {
Parser getParser(ContentType contentType);
}
- 我们配置
ServiceLocatorFactoryBean
使用ParserFactory
作为服务定位器接口,ParserFactory
这个接口不需要写实现类。
@Configuration
public class ParserConfig {
@Bean("parserFactory")
public FactoryBean serviceLocatorFactoryBean() {
ServiceLocatorFactoryBean factoryBean = new ServiceLocatorFactoryBean();
// 设置服务定位接口
factoryBean.setServiceLocatorInterface(ParserFactory.class);
return factoryBean;
}
}
- 设置解析器Bean的名称为类型名称,方便服务定位
// 设置bean的名称和类型一致
@Component("CSV")
public class CSVParser implements Parser { .. }
@Component("JSON")
public class JSONParser implements Parser { .. }
@Component("XML")
public class XMLParser implements Parser { .. }
- 修改枚举, 添加XML
public enum ContentType {
JSON,
CSV,
XML
}
- 最后用客户端调用,直接根据类型调用对应的解析器,没有了
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 控制反转的绝妙方法。它帮助我们解决了依赖注入未提供最佳解决方案的用例。也就是说,依赖注入仍然是首选,并且在大多数情况下不应使用服务定位器来替代依赖注入。
欢迎关注个人公众号『JAVA旭阳』交流沟通
Spring项目中用了这种解耦模式,经理对我刮目相看的更多相关文章
- 设计模式——Spring IoC中用到的模板方法模式
基本概念 什么是模板方法(Template method):父类定义了骨架(调用哪些方法及顺序),某些特定方法由子类实现. 最大的好处:代码复用,减少重复代码.除了子类要实现的特定方法,其他方法及方法 ...
- 在项目中用过Spring的哪些方面?及用过哪些Ajax框架?
在项目中用过Spring的哪些方面?及用过哪些Ajax框架? 解答:在项目使用过Spring IOC ,AOP,DAO,ORM,还有上下文环境. 在项目使用过Ext,Juery等Ajax框架.
- 项目中用到的SQL-总结
基本sql总结: Group by的理解:having子句,分组函数 Group by使用的限定: 1.出现在Select列表中的字段或者出现在order by后面的字段,如果不是包含在分组函数中,那 ...
- (转) Spring读书笔记-----部署我的第一个Spring项目
一.Spring介绍 Spring是一个轻量级的Java EE容器,它也是一种从实际需求出发,着眼于轻便,灵活,易于开发,易测试和易部署的轻量级开发框架.Spring它完成了大量开发中的通用步骤,留给 ...
- Spring读书笔记-----部署我的第一个Spring项目
一.Spring介绍 Spring是一个轻量级的Java EE容器,它也是一种从实际需求出发,着眼于轻便,灵活,易于开发,易测试和易部署的轻量级开发框架.Spring它完成了大量开发中的通用步骤,留给 ...
- spring项目log4j使用入门
log4j是Java开发中经常使用的一个日志框架,功能强大,配置灵活,基本上可以满足项目开发中对日志功能的大部分需求.我前后经历了四五个项目,采用的日志框架都是log4j,这也反应了log4j受欢迎的 ...
- JAVA WEB快速入门之通过一个简单的Spring项目了解Spring的核心(AOP、IOC)
接上篇<JAVA WEB快速入门之从编写一个JSP WEB网站了解JSP WEB网站的基本结构.调试.部署>,通过一个简单的JSP WEB网站了解了JAVA WEB相关的知识,比如:Ser ...
- Guava Cache探索及spring项目整合GuavaCache实例
背景 对于高频访问但是低频更新的数据我们一般会做缓存,尤其是在并发量比较高的业务里,原始的手段我们可以使用HashMap或者ConcurrentHashMap来存储. 这样没什么毛病,但是会面临一个问 ...
- Spring入门案例 idea创建Spring项目
spring入门案例 idea创建spring项目 Spring介绍 Spring概述 Spring是一个开源框架,Spring是2003年兴起的轻量级java开发框架,由Rod Johnson 在其 ...
- 我在一个前端项目中用js整理的一些通用方法,其中使用到的思想,主要就是约定了。
把名称和后台来的json数据约定起来,可以达到的效果就是可以将东西统一化,减少差异,提升模块等的通用性,此后就可以实现具体不同模块内容可以自动或拷贝赋值的方式 2016.7.18 refactor s ...
随机推荐
- Java 集合简介 一
什么是集合? 集合就是由若干个确定的元素所构成的整体.例如,5只小兔构成的集合: 在数学中,我们经常遇到集合的概念.例如: ● 有限集合 ○ 一个班所有的同学构成的集合: ○ 一个网站所有的商品构成的 ...
- 23.mixin类源码解析
mixin类用于提供视图的基本操作行为,注意mixin类提供动作方法,而不是直接定义处理程序方法 例如.get() .post(),这允许更灵活的定义,mixin从rest_framework.mix ...
- iOS- 最全的真机测试教程
想要上架的同学请看:<iOS-最全的App上架教程> 因为最近更新了Xcode 8 ,证书的创建都大同小异,只是在Xcode 8中的设置有一些变化,我就在下面补充,如有什么疑问,请联系 ...
- 关于Docker的一些事--Docker-Compose 升级版本
起源 近来一直在研究怎么搭建自己的私有网盘,本着虚心耐心,认真求是态度,开始做起了实验,最终种草了Nextcloud这款开源网盘,然而用私人的服务器感觉很卡,故转战到了一个基友的服务器,感觉非常吊! ...
- WinDBG详解进程初始化dll是如何加载的
一:背景 1.讲故事 有朋友咨询个问题,他每次在调试 WinDbg 的时候,进程初始化断点之前都会有一些 dll 加载到进程中,比如下面这样: Microsoft (R) Windows Debugg ...
- mybatis一对多映射分页的问题
一对多可能会出现分页错误 条数不对的问题 解决方法: 将主表分页查询一次 SELECT aa.id,aa.name,bb.name FROM (SELECT * from tab1 ORDER BY ...
- 2022-11-02 Acwing每日一题
逆序对的个数 给定一个长度为 n 的整数数列,请你计算数列中的逆序对的数量. 逆序对的定义如下:对于数列的第 i 个和第 j 个元素,如果满足 i<j 且 a[i]>a[j],则其为一个逆 ...
- Go实现常用软件设计模式三:生成器模式
目录: 举个栗子 概念介绍 使用场景 1.举个栗子 2.概念介绍 使用一个中间件来帮助我们填充创建对象参数 优点: 将创建逻辑集中在一起 复用了不同参数创建逻辑 缺点: 新增生成器类 3.使用场景 m ...
- php 高并发
1 swoole的博客 http://rango.swoole.com/archives/508 2 消息队列 3 接口并发请求
- go如何编写命令行(cli)程序
创建一个命令行程序 问题 如何使用golang创建可以在命令行当中传递参数的程序?go如何带参数执行程序? 比如我们期望使用hello -version来查看hello程序的版本号码.或者输入hell ...