Question

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?

Was it helpful?

Solution 2

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

OTHER TIPS

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

    ...
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top