August 28, 2023

Design Patterns: Strategy

Demystifying Design Patterns with Simple Explanations: Strategy

Design Patterns can be complex, but as Albert Einstein wisely said:
"If you cannot explain it simply, you don't understand it well enough." — Albert Einstein
So, let's demystify Design Patterns in plain language and with practical TypeScript examples, a coding language I've been delving into recently.

Why Design Patterns?

Software Engineering is a challenging field rife with problems. Often, these issues have already been tackled by someone else, and they've left us a guidebook called "Design Patterns."

The key point is this: don't reinvent the wheel. Be smart! Learn from those who've come before us and create something even better, building upon previous knowledge for the benefit of future generations of Software Engineers. This is how Software Engineering evolves.

Learn from past solutions, construct superior systems, and leave a lasting legacy for the next wave of Software Engineers. We're standing on the shoulders of giants!

The Problem

Let's paint a scenario: your application handles payment processing. The primary goal is straightforward – charge the client. However, there are multiple ways to achieve this, such as:

  1. Credit Card
  2. Debit Card
  3. Billet
  4. Automatic Bank Account Debits (e.g., GoCardless)
  5. Bitcoin
  6. And the list goes on...

In this case, we have algorithms that achieve the same objective but in different ways. While parts of their code may differ, their ultimate goal is identical – charging the client.

How do we elegantly solve this conundrum? Sure, we could have a single class responsible for processing payments using different methods. However, this approach burdens one class with multiple responsibilities, violating both the Single Responsibility Principle (SRP) and the Open-Closed Principle (OCP). Every new payment method would necessitate altering the Payment class.

So, what's the elegant solution?

The Strategy Pattern

Enter the Strategy Design Pattern, a Behavioral Design Pattern that aligns seamlessly with our predicament. This pattern employs a class that receives a "strategy" to accomplish a specific objective, making it a perfect fit for our case of charging clients in diverse ways.

To apply the strategy pattern, we'll need:

  1. An interface defining the structure that strategies must adhere to.
  2. A strategy class, responsible for invoking the particular method from the provided strategy.
  3. Multiple classes, each executing the same task in distinct ways.

Let's delve into the TypeScript code.

Types and Interfaces

First, we define the structure that strategies should follow. We consider a strategy as a way of accomplishing a task differently. This interface serves as the contract binding all strategies:

Types and Interfaces

Here we are going to define the structure that the strategies should follow. We consider a strategy for the class that will do the same thing differently. So, this interface will be the contract between all strategies:

Here we have a Payment type and the interface has just one single method: charge(), which should return a Payment.

That’s easy! Let’s check the different ways to charge a client and how they must follow this interface.

Charging with Debit card and Credit card

Here we should define the classes that will do the same thing in different ways. The same thing: charge the client. The different ways: one will do it with a credit card, and the other one will do it with a debit card.

They will look like this:

Each one implements the PaymentMethod interface, that’s why they both should have the charge() method. There are a lot of benefits by doing this:

  • Each class has its own business rules to do the same thing (Separation of Concerns).
  • Each class has its own responsibility (Single Responsibility Principle).
  • It’s easy to extend. If there’s another way to charge the client, we just need to create a new class implementing the PaymentMethod interface. That totally follows Open Closed Principe (OCP).

As you can see, this is an elegant way to split the responsibilities. However, how are they going to work? That’s why we are going to see the next class: the PaymentStrategy.

Payment Strategy

This is going to be a class where we can set up the strategy that we are going to use at that moment. We have different ways to do the same thing, but which one should we use? The strategy receives it!

So now we are totally able to set up the strategy that we want to use:

What if we want to use the debit card strategy? That’s easy!

Now we know an elegant solution to do the same thing in different ways. And that’s called: Strategy Design Pattern.

Talk with us!