自我审视记录本

这篇博客文章希望展示另一种方法,该方法如何通过依赖注入实现策略模式。 作为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)); }  } 
 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计算此类的使用情况)。

 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)); }  } 

最后一个问题是应用程序中的其他类是否需要在运行时选择存储库实现的可能性。 我可以将私有方法复制并粘贴到每个有此需求的类中,或者将地图的创建移至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

自我审视记录本

自我审视记录本_春天重新审视战略模式相关推荐

  1. 自我审视记录本_重新审视开源

    自我审视记录本 Sometimes all it takes is a step back from a technology sector to have a fresh (or refreshed ...

  2. 可见性得以保障,并不意味着_战略模式并不意味着春天!

    可见性得以保障,并不意味着 是的,所以可以说您正在编写一个Spring MVC应用程序,然后您决定:"我想做一些单独的封装算法,这些算法可以互换来执行特定的行为". 对此的经典回应 ...

  3. 战略模式并不意味着春天!

    是的,所以可以说您正在编写一个Spring MVC应用程序,然后您决定:"我想做一些单独的封装算法,这些算法可以互换来执行特定的行为". 对此的经典回应是"您需要一个战略 ...

  4. 宝宝起名神器小程序源码_支持多种流量主模式

    2022年马上到了,还不知道怎么给虎宝宝取名字么? 那么这款小程序源码就可以帮到你了,这款小程序支持输入姓氏自动起名. 不满意还可以点击换一换来找到满意的,支持起两个字或者三个字的名字. 另外也给该款 ...

  5. 中国现代服务业发展趋势及十四五战略模式研究报告2021-2027年

    中国现代服务业发展趋势及十四五战略模式研究报告2021-2027年 ****************************************************************** ...

  6. 修改文件夹名称隐藏文件夹_电脑中的上帝模式

    首页 » 其它 » 修改文件夹名称隐藏文件夹_电脑中的上帝模式 查看全文 http://www.taodudu.cc/news/show-5353664.html 相关文章: 新基建浪潮来袭 企业快速 ...

  7. 春天重新审视战略模式

    这篇博客文章希望展示另一种方法,该方法如何通过依赖注入实现策略模式. 作为DI框架,我选择Spring框架 首先,让我们看一下如何以经典方式实施策略模式. 作为起点,我们有一个HeroControll ...

  8. app 隐私 自我评估指南_监督和改善公司隐私和安全计划的一般法律顾问指南

    app 隐私 自我评估指南 Imagine that you are working as in-house or outside counsel for a business and you are ...

  9. 虚幻蓝图数据传递_数据产品的战略蓝图

    虚幻蓝图数据传递 In today's business climate, strategic moats are built with data. Long gone are the days wh ...

最新文章

  1. 用MFC制作程序启动logo
  2. pyqt5获取文本框里输入的值_实战PyQt5: 060-输入对话框QInputDialog
  3. MapHack源代码
  4. 洛谷 - P2472 [SCOI2007]蜥蜴(最大流)
  5. C++ socket编程 实现服务端与客户端的通讯
  6. 初学ActionScript 3.0(一):Hello World
  7. java阴阳师抽卡概率_《阴阳师》公布抽卡概率!看到数字我哭了
  8. windows php sqlite,如何在Apache 2.4(Windows 7)上为PHP 5.6.14配置SQLite3?
  9. opend和open的区别_open与open up的区别
  10. 鱼C论坛_VIP二号光盘
  11. 定义的form,宏等双击提示不存在的…
  12. C语言的数组简单复习
  13. 华为设备DHCP/DHCP中继及DHCPv6/DHCPv6中继详解
  14. 看单片机原理图-输入输出电路LED指示、按键输入
  15. 大数据时代的数据挖掘是怎么做的?
  16. linux下smbd服务,Linux下资源共享服务之samba 的讲解!
  17. 找出成绩全及格的学生(python)实现
  18. 该网站服务器出错了怎么回事,该网站服务器出错了是什么意思(图文)
  19. angular2后台管理系统
  20. 什么是BFC、IFC、GFC、FFC?

热门文章

  1. Acwing 217. 绿豆蛙的归宿
  2. P3959 [NOIP2017 提高组] 宝藏
  3. 【HDU-2376】Average distance
  4. 2020牛客国庆集训派对day2 MATRIX MULTIPLICATION CALCULATOR
  5. 洛谷P1912:诗人小G(二分栈、决策单调性)
  6. 51nod1836-战忽局的手段【期望dp,矩阵乘法】
  7. P7115-[NOIP2020]移球游戏【构造】
  8. CF461D-Appleman and Complicated Task【并查集】
  9. CF662C-Binary Table【FWT】
  10. JoyOI(TYVJ)1071-LCIS【线性dp,LIS,LCS】