春天重新审视战略模式

这篇博客文章希望展示另一种方法,该方法如何通过依赖注入实现策略模式。 作为DI框架,我选择Spring框架

首先,让我们看一下如何以经典方式实施策略模式。
作为起点,我们有一个HeroController ,应该在HeroRepository添加英雄, HeroRepository取决于用户选择的存储库。

 package com.github.sparsick.springbootexample.hero.universum;  import org.springframework.stereotype.Controller;  import org.springframework.web.bind.annotation.ModelAttribute;  import org.springframework.web.bind.annotation.PostMapping;  @Controller  public class HeroControllerClassicWay { @PostMapping ( "/hero/new" ) public String addNewHero( @ModelAttribute ( "newHero" ) NewHeroModel newHeroModel) { HeroRepository heroRepository = findHeroRepository(newHeroModel.getRepository()); heroRepository.addHero(newHeroModel.getHero()); return "redirect:/hero" ; } private HeroRepository findHeroRepository(String repositoryName) { if (repositoryName.equals( "Unique" )) { return new UniqueHeroRepository(); } if (repositoryName.equals(( "Duplicate" )){ return new DuplicateHeroRepository(); } throw new IllegalArgumentException(String.format( "Find no repository for given repository name [%s]" , repositoryName)); "Find no repository for given repository name [%s]" , repositoryName)); }  } 
 package com.github.sparsick.springbootexample.hero.universum;  import java.util.Collection;  import java.util.HashSet;  import java.util.Set;  import org.springframework.stereotype.Repository;  @Repository  public class UniqueHeroRepository implements HeroRepository { private Set<Hero> heroes = new HashSet<>(); @Override public String getName() { return "Unique" ; } @Override public void addHero(Hero hero) { heroes.add(hero); } @Override public Collection<Hero> allHeros() { return new HashSet<>(heroes); }  } 
 package com.github.sparsick.springbootexample.hero.universum;  import org.springframework.stereotype.Repository;  import java.util.ArrayList;  import java.util.Collection;  import java.util.List;  @Repository  public class DuplicateHeroRepository implements HeroRepository { private List<Hero> heroes = new ArrayList<>(); @Override public void addHero(Hero hero) { heroes.add(hero); } @Override public Collection<Hero> allHeros() { return List.copyOf(heroes); } @Override public String getName() { return "Duplicate" ; }  } 

此实现有一些陷阱。 存储库实现的创建不受Spring Context的管理(它打破了依赖注入/控制逆向)。 一旦您想使用需要注入其他类的其他功能来扩展存储库实现,这将很痛苦(例如,使用MeterRegistry计算该类的使用MeterRegistry )。

 package com.github.sparsick.springbootexample.hero.universum;  import java.util.Collection;  import java.util.HashSet;  import java.util.Set;  import io.micrometer.core.instrument.Counter;  import io.micrometer.core.instrument.MeterRegistry;  import org.springframework.stereotype.Repository;  @Repository  public class UniqueHeroRepository implements HeroRepository { private Set<Hero> heroes = new HashSet<>(); private Counter addCounter; public UniqueHeroRepository(MeterRegistry meterRegistry) { addCounter = meterRegistry.counter( "hero.repository.unique" ); } @Override public String getName() { return "Unique" ; } @Override public void addHero(Hero hero) { addCounter.increment(); heroes.add(hero); } @Override public Collection<Hero> allHeros() { return new HashSet<>(heroes); }  } 

这也打破了关注的分离。 当我想测试控制器类时,我不可能轻松地模拟存储库接口。 因此,第一个想法是将存储库实现的创建置于Spring上下文中。 库实现使用@Repository批注进行批注。 因此,Spring的组件扫描找到了它们。
接下来的问题是如何将它们注入控制器类。 在这里,Spring功能可以提供帮助。 我在控制器中定义了HeroRepository的列表。 在创建控制器实例的过程中必须填写此列表。

 package com.github.sparsick.springbootexample.hero.universum;  import org.springframework.stereotype.Controller;  import org.springframework.web.bind.annotation.ModelAttribute;  import org.springframework.web.bind.annotation.PostMapping;  import java.util.List;  @Controller  public class HeroControllerRefactoringStep1 { private List<HeroRepository> heroRepositories; public HeroControllerRefactoringStep1(List<HeroRepository> heroRepositories) { this .heroRepositories = heroRepositories; } @PostMapping ( "/hero/new" ) public String addNewHero( @ModelAttribute ( "newHero" ) NewHeroModel newHeroModel) { HeroRepository heroRepository = findHeroRepository(newHeroModel.getRepository()); heroRepository.addHero(newHeroModel.getHero()); return "redirect:/hero" ; } private HeroRepository findHeroRepository(String repositoryName) { return heroRepositories.stream() .filter(heroRepository -> heroRepository.getName().equals(repositoryName)) .findFirst() .orElseThrow(()-> new IllegalArgumentException(String.format( "Find no repository for given repository name [%s]" , repositoryName))); "Find no repository for given repository name [%s]" , repositoryName))); }  } 

Spring在其上下文中搜索HeroRepostiory接口的所有实现,并将它们全部放入列表中。 该解决方案的一个缺点是,每个添加了英雄的人都会浏览HeroRepository列表以找到正确的实现。 可以通过在控制器构造函数中创建一个以存储库名称为键,而对应的实现为值的映射来优化此映射。

 package com.github.sparsick.springbootexample.hero.universum;  import org.springframework.stereotype.Controller;  import org.springframework.web.bind.annotation.ModelAttribute;  import org.springframework.web.bind.annotation.PostMapping;  import java.util.HashMap;  import java.util.List;  import java.util.Map;  @Controller  public class HeroControllerRefactoringStep2 { private Map<String, HeroRepository> heroRepositories; public HeroControllerRefactoringStep2(List<HeroRepository> heroRepositories) { this .heroRepositories = heroRepositoryStrategies(heroRepositories); } private Map<String, HeroRepository> heroRepositoryStrategies(List<HeroRepository> heroRepositories){ Map<String, HeroRepository> heroRepositoryStrategies = new HashMap<>(); heroRepositories.forEach(heroRepository -> heroRepositoryStrategies.put(heroRepository.getName(), heroRepository)); return heroRepositoryStrategies; } @PostMapping ( "/hero/new" ) public String addNewHero( @ModelAttribute ( "newHero" ) NewHeroModel newHeroModel) { HeroRepository heroRepository = findHeroRepository(newHeroModel.getRepository()); heroRepository.addHero(newHeroModel.getHero()); return "redirect:/hero" ; } private HeroRepository findHeroRepository(String repositoryName) { HeroRepository heroRepository = heroRepositories.get(repositoryName); if (heroRepository != null ) { return heroRepository; } throw new IllegalArgumentException(String.format( "Find no repository for given repository name [%s]" , repositoryName)); "Find no repository for given repository name [%s]" , repositoryName)); }  } 

最后一个问题是应用程序中的其他类是否需要在运行时选择存储库实现的可能性。 我可以在有此需要的每个类中复制并粘贴私有方法,也可以将地图的创建移至Spring Context并将Map注入每个类。

 package com.github.sparsick.springbootexample.hero;  import com.github.sparsick.springbootexample.hero.universum.HeroRepository;  import org.springframework.boot.SpringApplication;  import org.springframework.boot.autoconfigure.SpringBootApplication;  import org.springframework.context.annotation.Bean;  import java.util.HashMap;  import java.util.List;  import java.util.Map;  @SpringBootApplication  public class HeroApplicationRefactoringStep3 { public static void main(String[] args) { SpringApplication.run(HeroApplication. class , args); } @Bean Map<String, HeroRepository> heroRepositoryStrategy(List<HeroRepository> heroRepositories){ Map<String, HeroRepository> heroRepositoryStrategy = new HashMap<>(); heroRepositories.forEach(heroRepository -> heroRepositoryStrategy.put(heroRepository.getName(), heroRepository)); return heroRepositoryStrategy; }  } 
 package com.github.sparsick.springbootexample.hero.universum;  import org.springframework.stereotype.Controller;  import org.springframework.ui.Model;  import org.springframework.web.bind.annotation.ModelAttribute;  import org.springframework.web.bind.annotation.PostMapping;  import java.util.Map;  @Controller  public class HeroControllerRefactoringStep3 { private Map<String, HeroRepository> heroRepositoryStrategy; public HeroControllerRefactoringStep3(Map<String, HeroRepository> heroRepositoryStrategy) { this .heroRepositoryStrategy = heroRepositoryStrategy; } @PostMapping ( "/hero/new" ) public String addNewHero( @ModelAttribute ( "newHero" ) NewHeroModel newHeroModel) { HeroRepository heroRepository = findHeroRepository(newHeroModel.getRepository()); heroRepository.addHero(newHeroModel.getHero()); return "redirect:/hero" ; } private HeroRepository findHeroRepository(String repositoryName) { return heroRepositoryStrategy.get(repositoryName); }  } 

这个解决方案有点丑陋,因为使用策略模式并不明显。 因此,下一个重构步骤是将英雄存储库地图移至自己的组件类。 因此,可以删除应用程序配置中的bean定义heroRepositoryStrategy

 package com.github.sparsick.springbootexample.hero.universum;  import org.springframework.stereotype.Component;  import java.util.Collection;  import java.util.HashMap;  import java.util.Map;  import java.util.Set;  @Component  public class HeroRepositoryStrategy { private Map<String, HeroRepository> heroRepositoryStrategies; public HeroRepositoryStrategy(Set<HeroRepository> heroRepositories) { heroRepositoryStrategies = createStrategies(heroRepositories); } HeroRepository findHeroRepository(String repositoryName) { return heroRepositoryStrategies.get(repositoryName); } Set<String> findAllHeroRepositoryStrategyNames () { return heroRepositoryStrategies.keySet(); } Collection<HeroRepository> findAllHeroRepositories(){ return heroRepositoryStrategies.values(); } private Map<String, HeroRepository> createStrategies(Set<HeroRepository> heroRepositories){ Map<String, HeroRepository> heroRepositoryStrategies = new HashMap<>(); heroRepositories.forEach(heroRepository -> heroRepositoryStrategies.put(heroRepository.getName(), heroRepository)); return heroRepositoryStrategies; }  } 
 package com.github.sparsick.springbootexample.hero.universum;  import org.springframework.stereotype.Controller;  import org.springframework.ui.Model;  import org.springframework.web.bind.annotation.GetMapping;  import org.springframework.web.bind.annotation.ModelAttribute;  import org.springframework.web.bind.annotation.PostMapping;  import java.net.Inet4Address;  import java.net.UnknownHostException;  import java.util.ArrayList;  import java.util.List;  import java.util.Map;  @Controller  public class HeroController { private HeroRepositoryStrategy heroRepositoryStrategy; public HeroController(HeroRepositoryStrategy heroRepositoryStrategy) { this .heroRepositoryStrategy = heroRepositoryStrategy; } @PostMapping ( "/hero/new" ) public String addNewHero( @ModelAttribute ( "newHero" ) NewHeroModel newHeroModel) { HeroRepository heroRepository = heroRepositoryStrategy.findHeroRepository(newHeroModel.getRepository()); heroRepository.addHero(newHeroModel.getHero()); return "redirect:/hero" ; }  } 

整个示例托管在GitHub上 。

翻译自: https://www.javacodegeeks.com/2019/09/strategy-pattern-revisited-spring.html

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

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

相关文章

[渝粤教育] 沈阳农业大学 有机化学 参考 资料

教育 -有机化学-章节资料考试资料-沈阳农业大学【】 1.1 有机化合物和有机化学随堂测验 1、【单选题】下列化合物中&#xff0c;不是有机化合物的是&#xff08; &#xff09; A、叶绿素 B、 氯仿 C、尿素 D、 氨气 参考资料【 】 2、【单选题】下列不是有机化合物的特点的是&a…

光纤收发器的选购原则介绍

光纤收发器做为一个区域网络连接器设备,其主要的任务就是怎样很好地把两方数据进行无缝连接。所以必须考虑其与周边环境相互兼容性的配合,及本身产品的稳定性、可靠性&#xff0c;反之&#xff1a;价格再低&#xff0c;也不能选用&#xff01;那么&#xff0c;光纤收发器的选购…

[渝粤教育] 洛阳理工学院 基础素描 参考 资料

教育 -基础素描-章节资料考试资料-洛阳理工学院【】 第一章 单元测验 1、【单选题】开创学院派素描教学体系的画家是谁 A、达芬奇 B、卡拉奇兄弟 C、安格尔 D、丢勒 参考资料【 】 2、【单选题】以下哪个不是素描常用工具 A、油画棒 B、铅笔 C、素描纸 D、橡皮 参考资料【 】 3…

[渝粤教育] 潍坊职业学院 化工安全技术 参考 资料

教育 -化工安全技术-章节资料考试资料-潍坊职业学院【】 危险化学品单元测试 1、【单选题】《危险货物分类和品名编号》&#xff08;GB6944-2012&#xff09;把危险化学品分为&#xff08; &#xff09;类。 A、8 B、9 C、10 D、12 参考资料【 】 2、【单选题】扑救爆炸物品火灾…

光纤收发器让网络布线变的更方便

光纤收发器是一种将短距离的双绞线电信号和长距离的光信号进行互换的以太网传输媒体转换单元&#xff0c;在很多地方也被称之为光电转换器。光纤收发器的应用让网络布线变的更方便&#xff0c;接下来我们就跟随飞畅科技的小编来详细了解下吧&#xff01; 企业在进行信息化基础…

maven和docker_与Maven和Docker的集成测试

maven和dockerDocker是其中的新热点之一。 与传统虚拟机相比&#xff0c;它具有一组不同的技术和思想&#xff0c;并通过容器的思想实现了相似但同时又有所不同的东西&#xff1a;几乎所有VM都具有强大的功能&#xff0c;但速度更快&#xff0c;并且还具有许多有趣的附加功能。…

[渝粤教育] 盐城师范学院 光学 参考 资料

教育 -光学-章节资料考试资料-盐城师范学院【】 判断题 1、【判断题】1-1下列说法正确吗&#xff1f;&#xff08;a&#xff09;相干叠加服从波的叠加原理&#xff0c;非相干叠加不服从波的迭加原理。 A、正确 B、错误 参考资料【 】 2、【判断题】1-1下列说法正确吗&#xff1…

如何从使用环境上区别光纤收发器?

根据针对不同环境&#xff0c;就需求不同性质产品。光纤收发器也一样有工业级和商业级之分&#xff0c;但是又区别于市场上TP-LINGK和D-link等(这些是家庭使用的3C电子产品很低端&#xff0c;大部分是回收二手货元器件生产&#xff0c;客户群体及应用的环境不一样)。在使用环境…

[渝粤教育] 苏州科技大学 混凝土结构设计原理 参考 资料

教育 -混凝土结构设计原理-章节资料考试资料-苏州科技大学【】 第1章测试 1、【单选题】均布荷载作用下简支梁上部受压下部受拉的试验被称为 A、路标试验 B、钢标试验 C、混标试验 D、简支梁试验 参考资料【 】 2、【单选题】钢筋混凝土结构中钢筋主要受&#xff08; &#xff…

保存您的lambda,以备不时之需-保存到文件

介绍 简短的帖子&#xff0c;描述了如何将Java lambda持久保存到文件中以在不同的过程中重复使用。 序列化Lambda Java 8中引入的Lambda使函数成为Java语言的一等公民&#xff08;几乎&#xff09;。 它们消除了使用专用类来保存函数的需要。 但是&#xff0c;这是如何进行的呢…

[渝粤教育] 西北工业大学 机械原理 参考 资料

教育 -机械原理-章节资料考试资料-西北工业大学【】 第一周单元测验 1、【单选题】手机属于。 A、机械 B、机构 C、机器 D、即不属机构&#xff0c;也不属机器 参考资料【 】 2、【单选题】下列常用装置哪些属于机器。 A、电动卷扬机 B、折叠烫衣板 C、手动窗扇开闭装置 D、汽车…

16槽双电源机架式光纤收发器产品优势介绍

双电源光纤收发器能满足远距离、高速、高宽带的快速以太网工作组用户的需要。产品性能稳定可靠&#xff0c;符合以太网标准&#xff0c;并具有防雷击保护措施。双电源光纤收发器适用于电信及各种宽带数据网要求高可靠性数据传输或组建IP数据传输专网的领域&#xff0c;是宽带校…

[渝粤教育] 西南交通大学 土木工程试验与量测技术 参考 资料

教育 -土木工程试验与量测技术-章节资料考试资料-西南交通大学【】 电阻应变片的粘贴及防潮工艺实验 1、【单选题】在测定石砌拱桥结构在车辆荷载作用下的应变时&#xff0c;最适合选用的应变传感器是&#xff08;&#xff09; A、电阻应变片 B、手持式应变仪 C、振弦式应变计 …

aws iam 架构图_使用IAM保护您的AWS基础架构

aws iam 架构图在开发新产品并发现合适的产品市场时&#xff0c;每个团队都需要快速行动。 尤其是初创公司&#xff0c;因为公司的整个未来都取决于快速找到为您的产品付款的人。 对于初创企业和其他团队来说&#xff0c; Amazon Web Services是令人难以置信的工具&#xff0c…

百兆光纤收发器和千兆光纤收发器的区别

百兆光纤收发器&#xff08;又名百兆光电转换器&#xff09;是一种快速以太网转换器。光纤收发器全面兼容IEEE802.3、IEEE802.3u、IEEE802.1d标准。支持全双工、半双工、自适应三种工作模式。 千兆光纤收发器&#xff08;又名光电转换器&#xff09;是一种快速以太网&#xff…

[渝粤教育] 西南石油大学 建设法规 参考 资料

教育 -建设法规-章节资料考试资料-西南石油大学【】 第一章单元小测 1、【单选题】法律关系产生的前提是( ) A、公民的法律意识 B、法律的存在 C、交易的发生 D、物的转移 参考资料【 】 2、【单选题】下列民事行为中属于代理的是&#xff08; &#xff09;。 A、由于事务繁忙&…

[渝粤教育] 西安建筑科技大学 环境规划与管理 参考 资料

教育 -环境规划与管理-章节资料考试资料-西安建筑科技大学【】 第一章单元测验 1、【单选题】1979年国家颁布了《中华人民共和国环境保护法&#xff08;试行&#xff09;》&#xff0c;( )实施了新的《环境保护法》 A、 2015年1月1日 B、2014年4月24日 C、 2015年4月24日 D、20…

常见光纤收发器组网方式介绍

在光纤网络布线中&#xff0c;光纤收发器的应用已经越来越普遍&#xff0c;仅需一芯光纤&#xff0c;便可组成一个环网&#xff0c;节约了布线成本&#xff0c;其中一个点出现故障&#xff0c;不影响其他点的使用。接下来我们就几种常见的光纤收发器组网方式进行简单的介绍&…

[渝粤教育] 郑州商学院 商学概论 参考 资料

教育 -商学概论-章节资料考试资料-郑州商学院【】 Chapter 1 单元测试 1、【单选题】_____ not only provide factors of production (or resources) but also consume goods and services. A、Suppliers B、Nonprofits C、Entrepreneurs D、Households 参考资料【 】 2、【单选…

[渝粤教育] 郑州航空工业管理学院 电工电子技术基础 参考 资料

教育 -电工电子技术基础-章节资料考试资料-郑州航空工业管理学院【】 小节测试 1、【判断题】任何一个完整的电路都必须有电源、负载和中间环节三个基本部分组成。 A、正确 B、错误 参考资料【 】 2、【判断题】电路的作用是对电能进行传输、分配和转换&#xff1b;而对电信号进…