春天重新审视战略模式
這篇博客文章希望展示另一種方法,該方法如何通過依賴注入實現策略模式。 作為DI框架,我選擇Spring框架
首先,讓我們看一下如何以經典方式實施策略模式。
作為起點,我們有一個HeroController ,應該在HeroRepository添加英雄, HeroRepository取決于用戶選擇的存儲庫。
此實現有一些陷阱。 存儲庫實現的創建不受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的列表。 在創建控制器實例的過程中必須填寫此列表。
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
總結
以上是生活随笔為你收集整理的春天重新审视战略模式的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 曹魏的首都在哪里(三国时期曹魏的都城在什
- 下一篇: maven和docker_与Maven和