avatarNicklas Millard

Summary

The article advocates for replacing traditional If-Else statements with the state pattern to create cleaner, more maintainable code.

Abstract

The article "APPLIED DESIGN PATTERNS: STATE" argues against the overuse of If-Else statements, suggesting that they lead to complex and unmaintainable code. It introduces the state pattern as a superior alternative for managing an object's changing state without resorting to convoluted branching logic. The author provides a detailed walkthrough on how to implement the state pattern in production-ready code, using a Booking class example with various states and transitions. The approach involves creating an abstract base state class, implementing each state as a separate class, and allowing the Booking class to delegate behavior to its current state object. This method is said to adhere to SOLID principles, improve code readability, and facilitate the addition of new features without altering existing code. The article also addresses concerns about persisting state objects in databases and reiterates the importance of using If statements judiciously, particularly as guard clauses.

Opinions

  • The author strongly believes that If-Else statements are a source of complexity and maintainability issues in code.
  • The state pattern is presented as a cleaner and more elegant solution for handling state-dependent behavior.
  • Creating new classes for each state is considered to enhance codebase maintainability and enjoyability, despite the increase in the number of classes.
  • The article suggests that complexity in code is not due to the number of classes but rather the responsibilities those classes hold.
  • The author emphasizes that If statements are not inherently bad, but the combination of If-Else can lead to maintainability headaches.
  • Persisting state objects in a database is not recommended; instead, the article advises mapping states to identifiers like type names, enums, or integers.
  • The author promotes the use of guard clauses with If statements to protect against invalid states or inputs.
  • The article encourages the adoption of the state pattern to align with SOLID principles and to make code more object-oriented and configurable.

APPLIED DESIGN PATTERNS: STATE

Stop Using If-Else Statements

Write clean, maintainable code without if-else.

You’ve watched countless tutorials using If-Else statements. You’ve probably also read programming books promoting the use of If-Else as the de facto branching technique.

It’s perhaps even your default mode to use If-Else. But, let’s put an end to that right now, by replacing If-Else with the state objects.

Note that you’d use this approach if you’re writing a class with methods that need its implementations to be changed depending on the current state. You’d apply another approach if you’re not dealing with an object’s changing state.

Even if you’ve heard about the state pattern, you might wonder how it is implemented in production-ready code.

Quick side-note: if you’re a visual learner, you can follow this new video that explains different approaches to eliminating if-else and switch cases:

For anyone who’s still in the dark, here’s a very brief introduction.

You’ll increase complexity with any new conditional requirement implemented using If-Else.

Applying the state pattern, you simply alter an objects behavior using specialized state objects instead of If-Else statements.

Gone are the days with code looking like this below.

Warning: PTSD trigger — also, hope you caught the logical error in here (other than the whole thing being a mess)

You’ve certainly written more complicated branching before. I have for sure some years ago.

The branching logic above isn’t even very complex — but try adding new conditions and you’ll see the thing explode.

Also, if you think creating new classes instead of simply using branching statements sounds annoying, wait till you see it in action. It’s concise and elegant.

Even better, it’ll make your codebase more SOLID, except for the “D” part tho.

🔔 Want more articles like this? Sign up here.

“Okay, I’m convinced If-Else is evil, now show me how to avoid messy branching code”

We’ll be looking at how I replace If-Else branching in production-ready code. It’s a made-up example, but the approach is the same I’ve used in codebases for large clients.

Let’s create a very simple Booking class, that has a few states. It’ll also have two public methods:Accept() and Cancel().

I’ve drawn a diagram to the best of my abilities that displays the different states a booking may be in.

Refactoring branching logic out of our code is a three step process:

  1. Create an abstract base state class
  2. Implement each state as a separate class inheriting from base state
  3. Let the Booking` class have a private or internal method that takes the state base class as a parameter

Demo time

First, we need a base state class that all states will inherit from.

Notice how this base class also has the two methods, Accept and Cancel — although here they are marked as internal.

Additionally, the base state has a “special” EnterState(Booking booking) method. This is called whenever a new state is assigned to the booking object.

Secondly, we’re making separate classes for each state we want to represent.

Notice how each class represents a state as described in the beautiful diagram above. Also, the CancelledState won’t allow our booking to transition to a new state. This class is very similar in spirit to the Null Object Pattern.

Finally, the booking class itself.

See how the booking class is simply delegating the implementation of Accept and Cancel to its state object?

Doing this allows us to remove much of the conditional logic, and lets each state only focus on what’s important to itself — the current state also has the opportunity to transition the booking to a new state.

How to deal with new conditional features?

If the new feature would normally have been implemented using some conditional checking, you can now just create a new state class.

It’s as simple as that. You’ll no longer have to deal with unwieldy if-else statements.

How do I persist the state object in a database?

You don’t.

The state object is not important when saving an object to e.g. an SQL or NoSQL database. Only knowing the object’s state and how it should be mapped to a column is important.

You can map a state to a friendly type name, an enum or an integer. Whatever you’re comfortable with, as long as you have some way of converting the saved value back into a state object.

But you’re still using IFs?

Yes — they’re essential. Especially when used as guard clauses. It’s the If-Else combination that is a root cause for maintainability headaches.

It’s a lot of additional classes!

Indeed. As I’ve mentioned in another article, complexity does not originate from the number of classes you have, but from the responsibilities those classes take.

Having many, specialized classes will make your codebase more readable, maintainable, and simply overall more enjoyable to work with.

There’s some truth to any joke.

Resources for the curious
--------------------------
Examples by Refactoring Guru
Examples by SourceMaking
How to make your code more Object-Oriented by Zoran Horvat
C# Design Patterns: State by Marc Gilbert

Nicklas Millard is a software development engineer in one of the fastest-growing banks, building mission-critical financial services infrastructure.

Previously, he was a Big4 Senior Tech Consultant developing software for commercial clients and government institutions.

New YouTube Channel (@Nicklas Millard)

Connect on LinkedIn

Programming
Software Development
Software Engineering
Technology
Csharp
Recommended from ReadMedium