大家好,我是陈总~不知道大家在项目中有没有遇到过这样的场景。根据输入的类型,调用接口不同实现类或服务,如根据文件类型使用CSV解析器或JSON解析器,一般使用ifelse对调用客户端进行判断,例如,如果类型等于JSON,我会使用JSON解析器,那么如果添加新类型的解析器,调用客户端是否需要修改?这显然太耦合了。本文介绍一种方法ServiceLocatorPattern来解决。它帮助我们消除紧密耦合的实现及其依赖性,并建议将服务与其具体类解耦。推荐给Java工程师的技术指南:https://github.com/chenjiabin...一个文件解析器的例子下面用一个例子来告诉你如何使用ServiceLocatorPattern。假设我们有一个从各种来源获取数据的应用程序,我们要解析不同类型的文件,比如解析CSV文件和JSON文件。1.定义一个枚举类型publicenumContentType{JSON,CSV}2.定义一个解析接口publicinterfaceParser{Listparse(Readerr);}3.根据不同的文件类型有不同的实现类//解析csv@ComponentpublicclassCSVParserimplementsParser{@OverridepublicListparse(Readerr){..}}//解析json@ComponentpublicclassJSONParserimplementsParser{@OverridepublicListparse(Readerr){..}}4.最后写一个调用client,通过switchcase根据不同的类型调用不同的实现@ServicepublicclassClient{privateParsercsvParser,jsonParser;@AutowiredpublicClient(ParsercsvParser,ParserjsonParser){this.csvParser=csvParser;this.jsonParser=jsonParser;}publicListgetAll(ContentTypecontentType){..switch(contentType){caseCSV:returncsvParser.parse(reader);案例JSON:returnjsonParser.parse(reader);..}}..}可能很大有人按照上面的方法实现了,也能正常运行。然后深思,有什么问题吗?现在如果产品经理提出新的要求支持XML类型的文件,客户端是否需要修改代码,在switchcase中增加一个新的类型,导致客户端和不同解析器之间的紧耦合。那么有什么更好的方法呢?应用ServiceLocatorPattern是对的,就是用我们的ServiceLocatorPattern。1.让我们定义我们的服务定位器接口ParserFactory,它有一个接受内容类型参数并返回Parser的方法。publicinterfaceParserFactory{ParsergetParser(ContentTypecontentType);}2.我们将ServiceLocatorFactoryBean配置为使用ParserFactory作为服务定位器接口。ParserFactory不需要为此接口编写实现类。@ConfigurationpublicclassParserConfig{@Bean("parserFactory")publicFactoryBeanserviceLocatorFactoryBean(){ServiceLocatorFactoryBeanfactoryBean=newServiceLocatorFactoryBean();//设置服务定位接口factoryBean.setServiceLocatorInterface(ParserFactory.class);返回工厂Bean;}}3.设置parsingimplementorbean的名称为类型名,方便服务定位//设置bean的名称和类型保持一致@Component("CSV")publicclassCSVParserimplementsParser{..}@Component("JSON")公共类JSONParser实现解析器{..}@Component("XML")publicclassXMLParserimplementsParser{..}4.修改枚举,增加XMLpublicenumContentType{JSON,CSV,XML}5.最后使用client调用,直接根据type调用对应的parser没有switchcase@ServicepublicclassClient{privateParserFactoryparserFactory;@AutowiredpublicClient(ParserFactoryparserFactory){this.parserFactory=parserFactory;}publicListgetAll(ContentTypecontentType){..//重点,直接根据类型获取returnparserFactory.getParser(内容类型).parse(阅读器);}..}嘿,我们已经成功地实现了我们的目标。现在添加新的类型,我们只需要扩展和添加新的解析器,我们不需要再修改客户端。封闭原则。推荐给Java工程师的技术指南:https://github.com/chenjiabin...如果你觉得Bean的名字直接使用类型很奇怪,这里我建议你按照下面的方法。publicenumContentType{JSON(TypeConstants.JSON_PARSER),CSV(TypeConstants.CSV_PARSER),XML(TypeConstants.XML_PARSER);私有最终字符串解析器名称;ContentType(StringparserName){this.parserName=parserName;}@OverridepublicStringtoString(){returnthis.parserName;}publicinterfaceTypeConstants{StringCSV_PARSER="csvParser";字符串JSON_PARSER="jsonParser";字符串XML_PARSER="xmlParser";}}@Component(TypeConstants.CSV_PARSER)公共类CSVParser实现解析器{..}@Component(TypeConstants.JSON_PARSER)公共类JSONParser实现解析器{..}@Component(TypeConstants.XML_PARSER)公共类XMLParser实现解析器{..}ServiceLocator模式浅析通过前面的例子,大家想必对ServiceLocator模式有了基本的了解和使用,下面我们来深入分析一下。服务定位器模式消除了客户端对具体实现的依赖。MartinFowler的以下引述总结了核心思想:“服务定位器背后的基本思想是拥有一个知道如何获取应用程序可能需要的所有服务的对象。因此,该应用程序的服务定位器将在返回时有一个“服务”方法。》推荐Java工程师技术指南:https://github.com/chenjiabin...Spring的ServiceLocatorFactoryBean实现了FactoryBean接口,创建了ServiceFactory服务工厂bean,综上所述,我们使用服务定位器模式实现了一个扩展的Spring控件出色的反转方法。它帮助我们解决依赖注入不能提供最佳解决方案的用例。也就是说,依赖注入仍然是首选,在大多数情况下不应使用服务定位器代替依赖注入。最后说点什么(勿嫖,求关注)陈的每篇文章都是用心输出,如果这篇文章对你有帮助,或者对你有启发,请帮忙点赞、观看、转发、收藏,你们的支持是我最大的动力坚持下去!关注公众号:【码猿技术专栏】,在公众号有超棒的粉丝福利,回复:进群,可以加入技术讨论群,和大家一起讨论技术,吹牛逼!
