Should strategy objects have state?
The Gang of Four Design Patterns book does have a few things to say about state being contained within the Strategy pattern.
There are two methods described for containing state:
- Provide the Strategy with any state information that it needs. The interface defines various methods and the parameters that are needed for the Strategy to perform its task.
- Allow the context to provide itself to the Strategy, either at the creation of the Strategy or when the Strategy is executed. The Strategy can access only the data that is needed through the specified interface.
The first option leads to a decoupled Strategy and Context. The second option leads to a tighter coupling between the Strategy and Context. You might want to consider the coupling/cohesion factor when choosing an implementation of Strategy.
Strategies increase the number of objects in an application. Sometimes you can reduce this overhead by implementing strategies as stateless objects that contexts can share. Any residual state is maintained by the context, which passes it in each request to the Strategy object. Shared strategies should not maintain state across invocations. The Flyweight (195) pattern describes this approach in more detail.
What this says is that typically, the state needed to carry out the Strategy is maintained within the context. This allows Strategies to be shared among various contexts without concern of an invalid state being contained. As long as the context has a valid state, that valid state will be guaranteed to be operated upon by the Strategy since it is provided as needed.
What's a good approach in this situation?
In your situation, I would consider not storing this information in the Strategy at all. It has nothing to do with the execution, but rather the result of the validation algorithm.
If you need to maintain data about success/failure, there are multiple points of failure, and you don't stop the algorithm after the first failure, I would consider the second implementation that I described above, where you pass the Context into the Strategy. Using a callback, the Context maintains a collection of validation errors. Every time a Strategy encounters an error, it simply calls a specified method in the Context to add information.
...it might need to perform two validations simultaneously...
Because of this, my approach would consist of a data structure such as a map in the Context. The map would be a mapping between Strategy and validation error, or perhaps a map between Strategy and a collection of validation errors.