Introduction
The strategy pattern is a behavorial design pattern that allows you to define a family of algorithms, encapsulate them as an object, and with the help of interfaces make them interchangeable. The use of type annotations also helps to make the code more readable and maintable.
The strategy pattern looks like this:

A short explanation:
- The context wants to apply some sort of algorithm, and for that purpose it contains an object which implements the Strategy interface.
- When the Strategy is needed an object of either StrategyA or StrategyB is initiated, and the algorithm method is called.
It could be argued that this is a form of dependency injection since methods are only called on an interface and not on concrete objects.
Implementation in Python
Before we start, we need to import some things. The TravelStrategy class which we will be defining and implementing is an abstract, there we need to import these first:
from abc import ABC,abstractmethod
What we do is import ABC which stands for ‘Abstract Base Class’. This will be the base class of all our abstract classes, or in this case just one. The abstractmethod decorator is used to denote methods as abstract. In practice this means that they will get an implementation in a subclass. However, this decorator also make sure this implementation exists, and will report an error if not.
We will start by defining a TravelStrategy interface:
class TravelStrategy(ABC):
@abstractmethod
def travel(self, distance: int) -> str:
pass
To keep things simple, we will define one method, travel(), which is used to simulate travelling by some sort of transport. Note that the distance has the annotation int to make sure only whole numbers are passed to this method.
Also not that this class derives from ABC so it is an abstract class. To make sure the travel method is implemented in the subclasses, we decorate it with the @abstractmethod decorator.
We now define our first strategy:
class TrainStrategy(TravelStrategy):
def travel(self, distance: int) -> str:
return f"Traveling {distance} km by train."
The class TrainStrategy is a child class of Strategy and therefore has to implement the travel() method. All this method does in our simplfied example is return a string containing the means of travel and the distance travelled.
The CarStrategy looks similar:
class CarStrategy(TravelStrategy):
def travel(self, distance: int) -> str:
return f"Traveling {distance} km by car."
Now we come to the heart of this pattern, the TravelHub class:
class TravelHub:
def __init__(self, longdistance_strategy: TravelStrategy, shortdistance_strategy: TravelStrategy):
self.longdistance_strategy: TravelStrategy = longdistance_strategy
self.shortdistance_strategy: TravelStrategy = shortdistance_strategy
def travel(self, distance: int) -> str:
if distance > 1000:
return self.longdistance_strategy.travel(distance)
else:
return self.shortdistance_strategy.travel(distance)
Some notes:
- We define two instance variables,
_longdistance_strategyand_shortdistance_strategyboth of typeTravelStrategy. - To the constructor we pass a long distance strategy and a short distance strategy, that way when a travel request comes, the travel hub can either simulate the travel or return an appropiate strategy.
- Lastly we define the travel method to simulate some kind of travel.
Time to test
Now we can test the code:
if __name__ == "__main__":
hub = TravelHub(TrainStrategy(), CarStrategy())
print(hub.travel(1500))
print(hub.travel(500))
Line by line:
- We construct a travel hub, passing the longdistance, and the short distance strategies. Note that these may be swapped or changed for something completely different.
- Next we simulate travel over 1100 kms and 120 kms.
Conclusion
The Strategy design pattern is a powerful and flexible tool for defining a family of interchangeable algorithms. As demonstrated in this article, it effectively encapsulates different behaviors—like traveling by train or car—into separate classes that all conform to a common interface, in this case, TravelStrategy. This approach promotes a more organized and maintainable codebase by decoupling the TravelHub from the specific travel methods it uses. The use of type annotations in Python further enhances code readability and helps prevent potential errors by ensuring that objects and parameters are of the expected types.
By allowing the TravelHub to dynamically select a travel strategy based on a condition (distance), the pattern exemplifies a core principle of object-oriented programming: favouring composition over inheritance. Instead of creating a rigid class hierarchy, the TravelHub composes its behavior by holding references to strategy objects. This makes it easy to add new strategies, such as PlaneStrategy or BusStrategy, without modifying the existing TravelHub class, thereby adhering to the Open/Closed Principle—that software entities should be open for extension but closed for modification. Ultimately, the Strategy pattern simplifies complex conditional logic, making the code more modular, flexible, and easier to scale.




