KarlKnechtel: A recent insight of mine related to StatePattern (or perhaps StrategyPattern too, given the similarity; see the discussion there).
Context: You intend to apply StatePattern to some class in your program. Because StateHasNoState, and you want to avoid LotsOfLittleObjects?, you'd like to be able to use some sort of FlyweightPattern too, so you create one instance of each possible state, and reference them as needed.
Problem: The algorithms applicable for each state need to know something about the containing object - or worse, about objects that the containing object knows about. If you just add a reference back to the containing object in your state object,
Solution: RefactorInterface
? of the state so that its methods (but only those which need it) take a
ContextParameter - i.e. a reference to the containing "context" as a parameter. So that the parameter can be provided, calling code always sends its messages to the containing object, which delegates to the state, providing a reference to itself when needed.
Side effects:
- You get a bit more documentation in code because you can see at a glance which methods are "context-sensitive" and which aren't.
- It might however be a PrematureOptimization; maybe you can stand to have LotsOfLittleObjects?, perhaps by refactoring things so the original "context" and the original "state" are unlinked and have a common parent, or perhaps by fixing the MutualHasaRelationship by MergeObject refactoring (if the total amount of state information is small).
- This also makes it a bit easier to create LawOfDemeter violations.
Examples:
- A graph of locations ("Tiles") in a game where each location may have something ("Contents") occupying it. This is a common model for puzzle games like SokoBan?.
- The player can interact with the contents of certain Tiles, and those interactions might depend upon Tiles adjacent to the contents being interacted with, and so on.
- The Tiles can locate their neighbours, and logically are mutually indistinguishable except for their neighbours and their Contents.
- There are several types of Contents (colours of blocks, items of food, weaponry, etc. - whatever), but Contents of the same type logically are mutually indistinguishable except by their location.
- So, the Contents become treated as state objects, and the interaction methods which need them take a Tile as a ContextParameter. The Tiles delegate to Contents; when necessary, the Contents use the interface of the Tile to find adjacent Tiles and thus interact with adjacent Contents.
- A user of a system needs to be provided a command-line interface.
- The interface is stateful: how a command is interpreted may depend on the interpretation of previous commands. For example, there might be a "default" parsing routine, and then some commands require a confirmation prompt which checks for a (yes/no) response. Each parsing method can be represented by a Parser object, with a "parse" method. So far, so good.
- The code's internal model for the user also has a state which may affect the interpretation of commands. It's not feasible to abstract this away into more Parsers representing the user state, perhaps because it smells logically, or perhaps because of CombinatorialExplosionOfClasses?. As well, the system is going to have a lot of users, who will change parsing state often.
- So: you set up your FlyweightFactory? for Parsers, and adjust your "parse" method to take a User as a parameter. Now User has-a Parser p, and a method parse(Input i) which delegates to p.parse(i, this). The Parser can generate a Command object, and provide the original User as an argument to the Command; in turn, the Command might update the User's state, up to and perhaps including the Parser. Alternately, you could have a RunAndReturnSuccessor model for the Parsers, or some more complicated cooperation between the Command hierarchy and the Parser hierarchy to figure out what comes next.