大家好,我是不才陈某~
不晓得大家在我的项目中有没有遇到过这样的场景,依据传入的类型,调用接口不同的实现类或者说服务,比方依据文件的类型应用 CSV 解析器或者 JSON 解析器,在调用的客户端个别都是用 if else
去做判断,比方类型等于 JSON,我就用 JSON 解析器,那如果新加一个类型的解析器,是不是调用的客户端还要批改呢?这显然太耦合了,本文就介绍一种办法,服务定位模式 Service Locator Pattern
来解决,它帮忙咱们打消紧耦合实现及其依赖性,并提出将服务与其具体类解耦。
举荐 Java 工程师技术指南:https://github.com/chenjiabin…
文件解析器的例子
咱们通过一个例子来通知你如何应用Service Locator Pattern
。
假如咱们有一个从各种起源获取数据的应用程序,咱们必须解析不同类型的文件,比方解析 CSV 文件和 JSON 文件。
1、定义一个类型的枚举
public enum ContentType {
JSON,
CSV
}
2、定义一个解析的接口
public interface Parser {List parse(Reader r);
}
3、依据不同的文件类型有不同的实现类
// 解析 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) {..}
}
4、最初写一个调用的客户端,通过 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);
}
2、咱们配置 ServiceLocatorFactoryBean
应用 ParserFactory
作为服务定位器接口,ParserFactory
这个接口不须要写实现类。
@Configuration
public class ParserConfig {@Bean("parserFactory")
public FactoryBean serviceLocatorFactoryBean() {ServiceLocatorFactoryBean factoryBean = new ServiceLocatorFactoryBean();
// 设置服务定位接口
factoryBean.setServiceLocatorInterface(ParserFactory.class);
return factoryBean;
}
}
3、设置解析器 Bean 的名称为类型名称,不便服务定位
// 设置 bean 的名称和类型统一
@Component("CSV")
public class CSVParser implements Parser {..}
@Component("JSON")
public class JSONParser implements Parser {..}
@Component("XML")
public class XMLParser implements Parser {..}
4、批改枚举, 增加 XML
public enum ContentType {
JSON,
CSV,
XML
}
5、最初用客户端调用,间接依据类型调用对应的解析器,没有了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);
}
..
}
嘿嘿,咱们曾经胜利地实现了咱们的指标。当初再加新的类型,咱们只有扩大增加新的解析器就行,再也不必批改客户端了,满足开闭准则。
举荐 Java 工程师技术指南:https://github.com/chenjiabin…
如果你感觉 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
的文章总结了核心思想:“服务定位器背地的根本思维是领有一个晓得如何获取应用程序可能须要的所有服务的对象。因而,此应用程序的服务定位器将有一个在须要时返回“服务”的办法。”
举荐 Java 工程师技术指南:https://github.com/chenjiabin…
Spring
的 ServiceLocatorFactoryBean
实现了 FactoryBean
接口,创立了 Service Factory
服务工厂Bean
。
总结
咱们通过应用服务定位器模式实现了一种扩大 Spring 管制反转的绝妙办法。它帮忙咱们解决了依赖注入未提供最佳解决方案的用例。也就是说,依赖注入依然是首选,并且在大多数状况下不应应用服务定位器来代替依赖注入。
最初说一句(别白嫖,求关注)
陈某每一篇文章都是精心输入,如果这篇文章对你有所帮忙,或者有所启发的话,帮忙 点赞 、 在看 、 转发 、 珍藏,你的反对就是我坚持下去的最大能源!
关注公众号:【码猿技术专栏】,公众号内有超赞的粉丝福利,回复:加群,能够退出技术探讨群,和大家一起探讨技术,吹牛逼!