Say I'm using spring, I have the following strategies...
Interface
public interface MealStrategy { cook(Meat meat); }
First strategy
@Component public class BurgerStrategy implements MealStrategy { @Autowired CookerDao cookeryDao; @Override public void cook(Meat meat) { cookeryDao.getBurger(meat); } }
Next strategy...
@Component public class SausageStrategy implements MealStrategy { @Autowired CookerDao cookeryDao; @Override public cook(Meat meat) { return cookeryDao.getSausage(meat); } }
Context...
@Component @Scope("prototype") public class MealContext { private MealStrategy mealStrategy; public void setMealStrategy(MealStrategy strategy) { this.strategy = strategy; } public void cookMeal(Meat meat) { mealStrategy.cook; } }
Now say this context was being accessed through an mvc controller, like...
@Autowired private MealContext mealContext; @RequestMapping(method = RequestMethod.POST) public @ResponseBody Something makeMeal(Meat meat) { mealContext.setMealStrategy(new BurgerStrategy()) mealContext.cookMeal(meat); }
Should the context be a component? When I do I get an error saying loadOnStartup an there's a nonUniqueBean that the strategy could be, as you'd expect. Do all of the beans need to be components like above or are my annotations incorrect?
My biggest query really is can you use a context like that in a Spring MVC app? The problem I have with using @Scope(prototype) too is it means the cookeryDao calls in the strategies return a null pointer as the Dao's don't get injected.
How would I implement the above pattern using spring and also be thread safe? Is what I'm trying even possible?
There is a lot of debate around the use of the Strategy Pattern with Spring. Often you'll see the Strategy Pattern used in conjunction with Dependency Injection, where Springs IoC container is making the choice of which strategy to use.
The Strategy Pattern is a behavioral design pattern that enables selecting an algorithm at runtime. I'll explain in this tutorial one way to implement it using the spring framework and leveraging its dependency injection power.
Since a concrete strategy is very often determined at run time based on the provided parameters or so, I would suggest something as follows.
@Component public class BurgerStrategy implements MealStrategy { ... } @Component public class SausageStrategy implements MealStrategy { ... }
Then inject all such strategies into a map (with bean name as a key) in the given controller and select respective strategy on request.
@Autowired Map<String, MealStrategy> mealStrategies = new HashMap<>; @RequestMapping(method=RequestMethod.POST) public @ResponseBody Something makeMeal(@RequestParam(value="mealStrategyId") String mealStrategyId, Meat meat) { mealStrategies.get(mealStrategyId).cook(meat); ... }
I would use simple Dependency Injection.
@Component("burger") public class BurgerStrategy implements MealStrategy { ... } @Component("sausage") public class SausageStrategy implements MealStrategy { ... }
Controller
Option A:
@Resource(name = "burger") MealStrategy burger; @Resource(name = "sausage") MealStrategy sausage; @RequestMapping(method = RequestMethod.POST) public @ResponseBody Something makeMeal(Meat meat) { burger.cookMeal(meat); }
Option B:
@Autowired BeanFactory bf; @RequestMapping(method = RequestMethod.POST) public @ResponseBody Something makeMeal(Meat meat) { bf.getBean("burger", MealStrategy.class).cookMeal(meat); }
You can choose to create JSR-330 qualifiers instead of textual names to catch misspellings during compile time.
See also:
How to efficiently implement a strategy pattern with spring?
@Resource vs @Autowired
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With