Suppose we have a class Player
with classes PhysicsComponent
, InputComponent
and StandingState
, DuckingState
.
The class Player
itself does not have an input function, the InputComponent
does. But when we change states to DuckingState
, for example, the behavior of the input function changes. So how is it set up?
a) The Player
class has a current_state
variable, which in turn has a variable for each component. This would create a lot of additional classes: StandingStatePhysicsComponent
, DuckingStatePhysicsComponent
, etc., since you'd want to "override" the input function, for example, you'd have to do that in the corresponding component class, which has that method.
b) The Player
class has a current_state
variable and a variable for each component. This would be possible by passing a lambda to each component when the state changes, that is executed on that component's update
method.
c) The Player
class has a variable for each component, which each in turn have a current_state
variable (This makes least sense to me, since each component would have it's own state).
Am I on the right track?
Best Answer
Be wary of YAGNI and consider this answer to a similar question which asserts that Patterns are not building blocks.
The GoF State Pattern solves a problem of selection of a single behavioural object to represent a class' functionality.
Meanwhile, the GoF Composite Pattern solves a problem of combining multiple behavioural objects to represent a class' functionality.
On that basis, the two patterns are designed for two almost-opposing objectives. One pattern seeks to single out just one behavioural object, while the other pattern seeks to combine multiple behavioural objects.
You may of course find a purpose for each of them somewhere in your program to solve different problems, but it's very difficult to see how you could gain any benefit from trying to use them together to solve the same problem.
Based on your description, it sounds like you could need either the Composite pattern or the State pattern, but probably not both (yet), and probably not both together (at least not directly).
Consider looking at how much variation in behaviour/logic you need from the state pattern, versus how much of your behaviour can simply be structured as data.
State Pattern
Component
classes (i.e. behaviour which follows different rules/algorithms/etc), then consider abandoning the composite pattern.Component
suggests those components have a very weak identity and shouldn't be treated as components because the state doesn't belong to the component, yet it deactivates large portions of its behaviour.State
classes instead, possibly passing data objects into them.State
class starts to look too big or takes on too many responsibilities, you could apply a composite pattern just to that particular state.Component Pattern
State
classesenum
variable which can act as the key to that dataset.Final (somewhat unrelated) note: Be wary of class names which sound like entities. Useful class names are often those which somebody reading your code can immediately relate either to some behavioural aspect of a program, or to a functional requirement.
Names like
Player
tell you nothing about what aPlayer
object should be responsible for; the real danger is that inevitably leads developers down an inside-the-box type mentality toward design; in turn that often results in God Objects which become responsible for doing everything that might conceivably relate to a player.