State Pattern with Real World Example

Feyyaz Yılmaz
Wingie / Enuygun Tech
4 min readApr 28, 2021

In this article, I write about how I solve a problem that I face when coding with state design pattern approach and show how this solution which is made by using this pattern increases quality of the code via some code blocks. Link for definition of state pattern is here

There are many landing pages in Enuygun Flight Project. These pages can be listed as:

  • Country(https://www.enuygun.com/ucak-bileti/tr-turkiye/)
  • Airline (https://www.enuygun.com/ucak-bileti/air-france/)
  • City (https://www.enuygun.com/ucak-bileti/istanbul/)
  • Airport (https://www.enuygun.com/ucak-bileti/izmir-adnan-menderes-havalimani/)

As you can see, contexts of those landing pages are different from each other. However, in terms of software, there is a structure which is expected to lead same pattern in routing mechanism and managed by redirecting different routes. Also, 301 redirection control is necessary to handle old versions (means name was changed because of some reasons) of city, country or airlines that are indexed in search engines in addition to routing management methods.

Because in Symfony project, routing that is written at the top runs in Controller or routing.yaml file, you can solve this issue in legacy code as below.

In this code, country data that is coming from slug to URL is checked from database. If there is no such data, whether this data is one of the indexed entities with old name in search engines is checked. If it is true, it is decided to redirect to new URL via 301. If these two conditions are not satisfied, it is forwarded to AirlineController in case of this slug might be an Airline.

There are similar codes in AirlineController, CityController and AirportController. However, there is a structure that returns 404 response code, if these two conditions are false in AirportController, which is different from AirlineController, CityController.

When we look at this structure, we see many repeating code blocks. State Pattern is an ideal solution to increase quality and readability of the blocks. Our States in according to this:

  • Found (State of finding data in database)
  • Redirect (State of finding data with old name and redirecting to new version)
  • Forward (State of redirecting to 2nd possibility when data is not in database or redirect table)
  • Not Found (State of all possibilities about data are wrong)

Definitions of StateInterface and States:

Each State decides road of Context (Country, Airline, City, Airport) that is coming as parameter by implementing proceedToNext method in StateInterface. Because there is no action to take for when Context is in Found or NotFound states, proceedToNext methods of the first and the last states (Found and NotFound) are empty here.

Now, it is time to define Contexts. Contexts are real element that makes landing page itself. It means Country, Airline,City and Airport. We define a Context as abstract and add dependencies that are required to arrange the States as property. Each context will define those property in constructor method.

We can explain parameters in AbstractContext that mentioned above as following:

  • State: State of Context
  • Redirect Route: Parameter that decided which route Context is redirected if it is in Redirect State (Actually it is itself but with a new slug)
  • Repository: Repository of Context. It will check whether it is available in database according to slug.
  • Redirect Parameters: Variable that decide with which parameters Context is redirected if it is in Redirect State.
  • Forward Parameters: Variable that decides which parameters Context is forwarded with if it is in Forward State.
  • Forward Controller: Variable that decides which Controller is redirected if it is in Forward State.
  • Slug: Variable coming from URL
  • Entity: Variable that keeps data coming from database.

Country Context is generated as following:

When we look at constructor method, slug coming from URL is checked with repository. If such country exists, entity data is set with value coming from database and no more work is done. Thus, state becomes Found which is the first status of constructive method of AbstractContext. If there is no such data in database, context state is set as Redirect and proceedtoNext method of this state is called. When we look at proceedToNext method in Redirect State, redirect table in repository is checked and if it is found state remains as Redirect, if it is not found State becomes Forward. forwarded conditions of Context of Country, Airline and City are all defined as true. This is because these are Contexts which transfer which state the data might be. For Airport context, this variable is set as null because this Context doesn’t have any state to forward, 404 response is required.

After defining Airline, City and Airport Context in similar ways, let’s look at how we use them in Controller. At first, we make a method that makes required redirection by taking final state of the context. We need to write this in controller because redirection methods are made in Framework Controller.

Now, let’s look at the real part where we put all together.

Country Context is generated and state of Context is determined by processing variable coming from URL thanks to state detection methods that run in constructive method.

For example, let’s assume air-france variable is coming from URL. Code snippet of CountryAction will generate CountryContext. air-france variable will be searched in country table of CountryContext. Because it is not found, state will become Redirect. Because it is also not found in Redirect table, state will become Forward. I redirect this to AirlineAction by putting this to routeStateDecider. When it goes to AirlineAction and same scenario runs again, it will find airline data in database and state will become Found. Data in AirlineAction will be build up and user will see https://www.enuygun.com/ucak-bileti/air-france/ landing page.

With this example, I solve a problem that I face in real life with State Pattern in a clean and qualified way.

If you want to join our team, share your CV with us: kariyer@enuygun.com

--

--