大家好,我是不才陈某~

不晓得大家在我的项目中有没有遇到过这样的场景,依据传入的类型,调用接口不同的实现类或者说服务,比方依据文件的类型应用 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@Componentpublic class CSVParser implements Parser {   @Override  public List parse(Reader r) { .. }}// 解析json@Componentpublic class JSONParser implements Parser {  @Override  public List parse(Reader r) { .. }}

4、最初写一个调用的客户端,通过switch case依据不同的类型调用不同的实现

@Servicepublic 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这个接口不须要写实现类。

@Configurationpublic 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

@Servicepublic 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...

SpringServiceLocatorFactoryBean实现了 FactoryBean接口,创立了Service Factory服务工厂Bean

总结

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

最初说一句(别白嫖,求关注)

陈某每一篇文章都是精心输入,如果这篇文章对你有所帮忙,或者有所启发的话,帮忙点赞在看转发珍藏,你的反对就是我坚持下去的最大能源!

关注公众号:【码猿技术专栏】,公众号内有超赞的粉丝福利,回复:加群,能够退出技术探讨群,和大家一起探讨技术,吹牛逼!