5

Both here on stack overflow and on Java Effective it is suggested that strategy design patterns should be stateless. In fact in the book it is also suggested to make each strategy object a singleton.

The problem I have is that some strategies I envision for my program need states/fields. Either because they are path-dependent in their behavior or because I want them heterogeneous (a statistical distribution of similar strategies, if you prefer).
This forces me to break both Java Effective suggestions: I instantiate a new strategy for each user class AND each of these strategies contain its own fields.
Is that very bad? Should it be done differently?

It was suggested to me to keep the fields that make the strategy heterogeneous in the class that uses it and then pass it as an argument. I find that very anti-oo. Those fields don't belong to the user class. In fact, if that class uses another strategy it might not need those fields at all. That seems to run against the reason I am using the strategy pattern in the first place.
Mostly I am just very confused


I make a simple example here. Imagine you have a class Gambler, which represents somebody making bets on horses. Now this class will require a strategy predictStrategy that will work something like this:

interface predictStrategy{
    public Horse predictWinningHorse(HorseRace r);
}

Now, I can have many implementations where the strategy is to choose at random, or pick the white horse or whatever. That's easy.
Imagine though that I implement a strategy that looks at past predictions and somewhat "learns" from its past mistakes. Clearly each strategy will have to have its own memory on which to learn. I might have to add one more method to the interface (or make an extension)

interface predictStrategy{
    public Horse predictWinningHorse(HorseRace r);

    public void addObservation(HorseRace r, Horse oldPrediction, Horse actualWinner);
}

So that the Gambler class calls "strategy.addObservation(...)" at the end of each race to improve its predictive power.
Can this be really be done with a stateless strategy object? It seems impossible to me.

4
  • 3
    it would be great if you could provide a code sample of the use of strategy pattern in your context
    – Duli-chan
    Commented Jul 27, 2012 at 2:11
  • 1
    I tried providing an example. I hope it helps. Thank you so much for your support!
    – CarrKnight
    Commented Jul 27, 2012 at 3:00
  • What fields (ie "state") are you considering having in your strategy instances?
    – Bohemian
    Commented Jul 27, 2012 at 3:12
  • Well, for the example given I'd need a List of observations and past predictions, for example. Or whatever it is that the strategy is learning. It would depend on the statistical model used in this case.
    – CarrKnight
    Commented Jul 27, 2012 at 3:16

2 Answers 2

3

I would argue that each instance should remain stateless once it is constructed, i.e., it should not maintain extra state across invocations that changes based on the parameters. A classic example is a tax calculator.

interface TaxCalculator {
    /**
     * Calculates the tax for the given purchase price and shipping charges.
     * All values are in pennies to avoid rounding.
     *
     * @param subtotal total price of all items ordered
     * @param shipping cost of shipping the order
     * @return calculated tax
     */
    int calculate(int subtotal, int shipping);
}

class NoTax implements TaxCalculator {
    public int calculate(int subtotal, int shipping) {
        return 0;
    }
}

class FixedPercentOfSubtotal implements TaxCalculator {
    private final int ratePercent;

    public FixedPercentOfSubtotal(int ratePercent) {
        this.ratePercent = ratePercent;
    }

    public int calculate(int subtotal, int shipping) {
        return subtotal * ratePercent / 100;
    }
}

While FixedPercentOfSubtotal has a member (state), it's provided at construction time and never changes. You could store one instance per state, making them quasi-singletons.

Update

Neither the Wikipedia article nor this Strategy Pattern page make any stipulation that implementations should not maintain state across calls. It's less common because strategies are designed to be interchangeable and pluggable at runtime, but I wouldn't rule it out.

However, that you needed to add a new method to enable that one strategy implementation is a red flag. Other implementations may not need it. Will you define it in the interface anyway? It may make more sense to have the historical-tracking strategy implement RaceListener and add it to the race track instance. This would allow it to be shared among multiple users as a singleton.

2
  • Thank you! This is how it was explained to me, great. But that's perhaps not exactly what I was looking for. Does the example I added explain my question better?
    – CarrKnight
    Commented Jul 27, 2012 at 3:00
  • @CarrKnight - Does my update address your example? Commented Jul 27, 2012 at 6:02
2

In your example of the horse prediction strategy, you have mixed strategy and history analysis.

The solution is to record the horse history somewhere else, and simply replace the existing strategy with one created from different parameters calculated (by another, factory class) from past performance.

That way you have three distinct classes:

  • Strategy (stateless and immutable)
  • Prediction history collector
  • Strategy factory (that analyses the history to fine tune the parameters used to create a new Strategy)

Each with their own independent, and unrelated, concerns. You can chose how often to swap out the strategy instance with a new (hopefully better) one.

3
  • yup, that's what I am doing now. The problem I have is that what the "history" is depends very much on the strategy. Some need no history, some need just to check how many times were correct and some need to store a whole host of variables to regress against. Also, in some cases I have a Gambler alternating between strategies. When I keep all history together I am unable to tell which strategy is predicting better (which is important) or what part of the data to ignore. Thank you!!
    – CarrKnight
    Commented Jul 27, 2012 at 3:27
  • 1
    Store the histories in a map of strategy-parameters --> history
    – Bohemian
    Commented Jul 27, 2012 at 4:34
  • Again, my policy exactly. But it's getting very heavy on the memory and it lays mostly unused.
    – CarrKnight
    Commented Jul 28, 2012 at 1:09

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.