How the State Pattern Will Improve Your Go Programs

Cyberlurk
The Startup
Published in
12 min readJan 22, 2021
How to implement the State Pattern in Go

The State Pattern is considered a Behavioural Design Pattern. Behavioural patterns seek to ensure that objects may still exchange messages and at the same time be loosely coupled. This is enforced by proper assignment of responsibilities between objects. In case of the State Pattern this is implemented by letting an object alter behaviour when its internal state changes — sometimes to such an extend that it may look like the object has changed its class. In plain English - The State Pattern might be used to improve your code significantly, making it easier to maintain and extend the code when new features must be added to the code base.

Who should read this article?

I assume that the reader is rather familiar with programming but is interested in learning more about design patterns. For this reason some parts of the code is not explained in details, since I have assumed that the reader already has a solid understanding of programming. The examples are written in Go but the state pattern can be applied to any language that supports polymorphism.

The Project

Together we will build a traffic light — twice.
For the purpose of highlighting why the State Pattern is important to know of I will first demonstrate which issues may arise if we do not use it where called for. After the initial demonstration we will talk about the code’s limitations. Then finally, we are going to discover a much better solution by using polymorphism to implement the State Pattern. Our final solution will adhere to the SOLID principles as well. In this article we will mainly focus on the first two principles.

The Single Responsibility Principal

A function shall do one thing only — and do it well. Having only one responsibility a class, struct or function should have only one reason to change. If there are several reasons that a function or a struct might need to be changed later on it is best to separate the code into multiple smaller chunks - each being responsible for a core task.

Instead of cramming a lot of different functionality into a single class, struct or function, one must seek to separate code into less complex constructs and place them in individual classes, structs and functions.

Open-Closed Principle

Applications shall allow for easy behaviour extension without modifying existing code as it can lead to bugs when working code is being altered.

The first implementation of the traffic light is not loyal to these important coding guidelines.

Enough theory. Now we are going to take an adventurous dive into the warm waters of the State Pattern by being pragmatic and getting our hands dirty — or should I say wet? We are going to learn by writing code.

Let us get started, shall we?

Simple Implementation

For building the first traffic light we will take a commonly used object oriented approach and code it as a less experienced programmer might approach the task given. The code is concise and meaningful, though a little naive, which we will realize later in the project. The problems will reveal themselves when the code needs to be extended later on.

Because writing white monospaced text in a black console background is a little mundane we will visualize the traffic light by making use of a tiny game library called Pixel. I promise, we will end up with enough beautiful colours and flashing lights to start an epileptic seizure. On a serious note though, we need a bit of complexity to really see how things are being simplified later on.

We will start by defining our traffic light struct.

We create a SimpleTrafficLight struct. The struct contains a Light field and two fields that is needed for drawing to the screen. According to best practice we provide a constructor for our traffic light. The constructor takes two parameters that is needed for drawing the traffic light to the screen. Initially the traffic light will show a red light as the Light field is set to RED.

We then go on and create a handful of methods for our traffic light of which the most interesting methods are NextLight and Draw. These methods are responsible for changing the traffic light from one Light mode to the other and drawing the correct lights to the screen, respectively.

The rest of the methods are rather self explanatory and are used for drawing coloured circles to the screen, which represents the traffic light. This gives us an easy way to tell how our traffic light is currently operating.

Fairly simple, right? Now let us move on to the main loop that is responsible for making the traffic light change colour between loop cycles and, once per cycle, call the traffic light’s Draw method.

As can be seen in the above code view we create an instance of SimpleTrafficLight that is then used inside an infinite loop. Inside the loop we make use of the traffic light’s NextLight method in cohesion with the Draw method of the traffic light object. In between these calls we sleep for a second. Sleeping the whole thread is considered bad practice, as it will freeze the window, but we try to keep the code simple as our focus is on the use of design patterns. Later on we improve the interval code to avoid the window from being unresponsive while the traffic light is waiting for the right time to change and draw a new colour mode.

Compile and run the code and you should see a traffic light that changes the colour mode each second.

Add first new requirement

Looks awesome, but there is a problem with this. To highlight the exact issues with our current code and clarify the aforementioned principles we will add new functionality to our current traffic light. Let us imagine that one of our customers, a municipality, has demanded that we update all the traffic lights to adhere to a new set of requirements. The municipality has just bought physical car speed scanners for all the traffic lights in one of its cities. The scanner is a modern IoT device which measures a car’s speed. The scanner supports several communication protocols so that it may notify a traffic control system of how fast a car is going at a particular section of a road. The idea is that a traffic light equipped with a scanner can detect cars running for red light and cars driving too fast. The traffic light may then react upon this information by taking a photograph of the driver and the license plate and send it to the nearest police station.

To make use of these new scanners our traffic light must now support being notified of a passing car’s speed as it enters the intersection. The municipality has therefore demanded that we add new traffic light methods:

(tl *SimpleTrafficLight) func CarPassingSpeed(speed int, licensePlate string)

func reportDriver()

func redLighRunner()

Further requirements for the method is as follows. Cars passing above the speed limit should be reported with a function call to reportDriver except cars running for red which must always be fined no matter the speed.

Take a close look at the code and think for a minute about how you would write the content of the new required method. When you have thought about it for a while take a look at the solution below that I have seen programmers choose in similar situations.

The solution makes use of a new switch statement with all the different light modes as cases. I guess that you’re already seeing the issue here?

Add a second new requirement

To highlight the issue with our current code and clarify the aforementioned principles we will worsen our code even further. Imagine a second requirement being demanded for by our customer. Due to city busses getting stuck during heavy morning traffic the traffic light now needs to be extended with a special light mode that would allow busses to enter the intersection before the cars. According to local law a purple circle placed to the left of the green circle means that buses may enter the intersection and turn left before all other vehicles. The buses are driving in designated bus lanes towards the traffic light.

The light must now change from RED_AND_AMBER to PURPLE to GREEN.

Again, I will ask of your to consider how you would solve this coding puzzle. Have a glance at the code below when you have thought about it. The code shows how one might extend the code with this new functionality.

The purple bus light has been added to our range of enums and we have added the new light mode to all our switch statements. Yeah, it isn’t pretty. Furthermore some more logic was needed in CarPassingSpeed as we now need to distinguish between cars and busses. Busses shouldn’t be fined for entering the intersection when the purple light is shown, but all other vehicles must. Also CarPassingSpeed must be called from our main loop every time a car enters the intersection.

Issues with the current solution

One might ask two questions to determine whether code should be refactored.

Do I have the same if/switch statement in various places?

Do I find myself making the same change to the same if/switch in several places?

Unfortunately we can answer yes to both questions above and that means we need to consider reorganizing our code as we are in conflict with the principles of DRY (don’t repeat yourself) and SOLID that were mentioned earlier in this article. Not adhering to the principles causes the programmer to copy a switch statement when some new requirements are added as we saw when implemented the first requirement. Adding other requirements have the unfortunate side effect that the programmer must make changes to already working code. This happened to us when we extended the code base the second time.

Furthermore we have logic for different functionality placed within the same functions, which makes it complicated to read and understand the code. This begins to be noticeable when we added the new logic for the purple bus light where a fine should be given only in some circumstances. We have logic for the different light modes close to each other due to everything being contained within the same switch statement.

State Machine Implementation

Sometimes it is less obvious that a program is operating in states but in the case of a traffic light it is quite obvious. I would argue that a traffic light begs to be implemented as a state machine as it clearly has several states. Which is also the reason why the traffic light was chosen as an example for this article. Throughout this article we have defined the different lights in the traffic light as light modes. They should in fact be thought of as states. The traffic light should exhibit different behaviour depending on which state is active. Only one state may be active at a time.

We will now rewrite our traffic light to adhere to the State Pattern. The concept of this pattern is, that at any given time, there’s a clearly defined number of states which a program may be in. The programs behaviour changes depending on which state is currently active. Multiple states may exists, but only one at a time. Going from one state to another is called a transition and may happen at any given time. This is normally controlled from within the state themselves; hence each state must know which other state it can transition to.

UML diagrams can show state. A state diagram of a traditional traffic light is shown below.

States are represented as boxes and the transitions are represented by arrows. Arrows may have text descripting which event that invoked the transition. The black circle is the beginning of our program execution.

Enough of the theory, let’s start rewriting our program.

First we create an interface type called LightState and we use this interface in a field called State in our TrafficLight struct. We add a method to TrafficLight called TransitionState. This method is called by a state when the traffic light state should be changed. A state might need some initialization; hence all states are required to implement an EnterState method. This method is called immediately after a transition. Optionally, you could have a Leave method too, however that won’t be needed now. Each state must also have a Draw and a NextLight method. Notice how the methods takes a TrafficLight pointer as their parameter. In this way the states can interact with the TrafficLight they are a part of.

As a result our main file must now call these methods on the traffic light’s state instead of on the traffic light directly.

All states may now exist as its own struct written in its own file.

We will create these files

  • red_state.go
  • red_amber_state.go
  • amber_state.go
  • green_state.go

In this way we separate logic for each state, which makes the code easier to maintain. Debugging can be done faster as well as we know precisely which part of the code that caused the error if the bug happens in a specific state.

We make use of a little optional trick to allow our states to have default methods. The trick is to create a default state which we will call DefaultLightState. This state is not supposed to be used on its own and as such it does not implement all the required state methods. Without having a default state to build upon we might had to implement empty methods to satisfy the interface in all our states, even if we didn’t need any logic in these. For instance, this could happen if the State interface required all states to have a Leave method that we never really used.

Add the following to the trafficlight.go file.

For debugging and demonstration purposes we have given states a StateName field and a default EnterState method that will print out the name of the current state as soon as the traffic light has transitioned from one state to another. A default CarPassingSpeed method is also provided by the default state. Otherwise we would have to replicate this throughout several of the stages. The red state will override the default CarPassingSpeed as no cars should be passing a red light — no matter the speed. For this reason the red state needs its own implementation.

Now, how do we extend a struct in Go. We can achieve this by use of what Go calls embedded fields. A field declared with a type but no explicit field name is called an embedded field. This is best shown by example. Have a look at the new red light state.

See how we made use of the aforementioned embedded field in the redState struct?

Important to notice as well is the method NextLight. This shows how we can transition from one state to another, which should be controlled by the states. Often you wouldn’t have a method called something like NextLight or NextState. The transition would happen from within the state method and happen due to some control logic deciding when it is time to transition. One state may have different states to transition to depending on various variables. In the case of our traffic light, we have a very linear transition flow and I have decided that it was okay for the state to expose a transition method of its own, though this isn’t as common.

Now the other states must be created in a similar manner and you’re all set.

I have left out the implementation of the bus lane light as an exercise for the reader. I recommend that you get yourself acquainted with the state pattern by extending the code by adding this light yourself.

Final words

Hopefully you now have a firm understanding of how and when to use the state pattern in your code projects. I hope that you enjoyed reading the article and that you now have an inner craving for embedding the state pattern into your own projects henceforth. If you appreciated the article please give an applause by clicking the hands icons so that more programmers may be presented to the article.

GitHub

Both traffic light implementations can be found on my GitHub repository.

The main loop’s sleep call has been substituted with a select statement in the second implementation in the repository. In this way the windows is still responsive while in between states.

--

--