design-patternsstate-pattern

How to prevent a context class using the state design pattern from having too many responsibilities?


I am making use of the state design pattern for a project of mine where I have a Car class which is the context class and it can have 3 states - neutral, drive and park. In each of those states, the car can execute different actions such as in Neutral, you can play a movie in the car, in Drive you can do something else and in Parked as well.

Now, I can see the benefits of the state design pattern, not using too many if statements and keeping logic separate for each state and making it easily extensible code but after some thinking I am not sure how to prevent the Context class from having way too many responsibilities. At the moment I have just a few actions but in the future I might want to add like 5-6 actions per different state and then my Context class will be full, in addition to keeping track of the speed of the car, fuel etc.

First of all, is that a problem and second, if yes how to prevent that?

I tried researching online but could not see any articles on the Context class having too many responsibilities which led me to thinking that maybe this is normal but still I am not completely sure.

Here are places I went to to search for information regarding this pattern:

RefactoringGuru: https://refactoring.guru/design-patterns/state/java/example

DoFactory: https://www.dofactory.com/net/state-design-pattern

Baeldung: https://www.baeldung.com/java-state-design-pattern

I went to a few more places but the information more or less is the same. They go give general examples but in none of them I can see clearly defined what is inside the Context. I see that it is passed as an parameter to the specific state objects and they use it to call private fields or a method but that does not answer my question.


Solution

  • I would argue that you have missed one of the point of the State pattern. From the Wiki article:

    Implementing state-specific behavior directly within a class is inflexible because it commits the class to a particular behavior and makes it impossible to add a new state or change the behavior of an existing state later, independently from the class, without changing the class. In this, the pattern describes two solutions:

    • Define separate (state) objects that encapsulate state-specific behavior for each state. That is, define an interface (state) for performing state-specific behavior, and define classes that implement the interface for each state.
    • A class delegates state-specific behavior to its current state object instead of implementing state-specific behavior directly.

    So the idea is to encapsulate the state behaviour outside the context class and move it to concrete state implementations.

    Consider the following diagram:

    enter image description here

    The context class will delegate actions (requests) to the concrete states.