1500字范文,内容丰富有趣,写作好帮手!
1500字范文 > 自我审视记录本_春天重新审视战略模式

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

时间:2024-07-03 05:56:15

相关推荐

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

自我审视记录本

这篇博客文章希望展示另一种方法,该方法如何通过依赖注入实现策略模式。 作为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.ponent; 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 .Inet4Address; import .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上 。

翻译自: //09/strategy-pattern-revisited-spring.html

自我审视记录本

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。