Developers usually spend more time maintaining and adding new features into an existing system instead of building new ones. It is so critical to have a good system design that is able to accommodate changes. In this post let’s go through one of the most adopted design patterns by starting with a problematic design and evolving gradually to make our system extensible and reusable. What is Strategy Pattern? A high-level explanation would be The Strategy Pattern defines a family of algorithms, encapsulates each one, and makes them interchangeable. Strategy lets the algorithm vary independently of clients that use it. It must be confusing with the above abstraction, but don’t worry, in the following, we will walk through an example and identify the problems and make progress step by step to understand the key principles of the strategy pattern. Consider an Example Let’s say we want to introduce a class of Ducks, which includes , and etc. Each of them would have some commonly shared functionalities like, , , and , take a minute and think about how would you address this problem in your system design? ( ) redhead duck mallard duck swim display fly code here The easy piece, right? The first thing we can leverage in OO programming is to leverage inheritance, we would have a superclass called and have other subclasses inherit the functionalities of our parent class. Something like this, Duck class Duck(ABC): def __init__(self): pass @abstractmethod def display(self): pass @abstractmethod def swim(self): pass @abstractmethod def quack(self): pass class RedheadDuck(Duck): def swim(self): return "paddling" def quack(self): return "redhead quack" def display(self): return "redhead duck" Looks all good! But now there comes a new requirement, we want to introduce a new functionality to our ducks, which is . What do we do about it? We can have a method in our superclass and all child classes would inherit this method automatically. fly fly class Duck(ABC): def __init__(self): pass @abstractmethod def display(self): pass @abstractmethod def swim(self): pass @abstractmethod def quack(self): pass def fly(self): return "fly with wings" That seems no harm, but in the meantime, our products introduced a new kind of duck, , by inheriting the class, what we would see is, rubber duck Duck class RubberDuck(Duck): def display(self): return "rubber duck" def swim(self): return "floating" def quack(self): return "jioooo" rubber_duck = RubberDuck() print(rubber_duck.fly()) # "fly with wings" Our is flying! Which makes no sense, what would you do to solve this issue? rubber duck Of course, we can the method in our rubber duck and make it can not fly, but is it efficient in our future development? override fly() What if in the future we are going to introduce more non-flyable duck types, , , should we override the method one by one? wooden duck robotic duck fly() Principle One Here comes our first design principle. Identify the aspects of your application that vary and separate them from what stays the same. This is basically saying take the part that would change a lot in the system and make it independent. In our case, since is a method that going to vary a lot in our different duck types, we can take it out and make it a separate and for flyable ducks, they would inherit this class if needed. fly class Hold on for a minute and reflect on the design, is everything right? If we have a concrete implementation of the fly method in a separate class, then every subclass will have the same fly behaviour, what if there are other ducks fly in a different way? Flyable To solve the above issue there comes our second principle. Principle Two Program to an interface, not an implementation. The point is to exploit polymorphism by programming to a supertype so that the actual runtime object isn’t locked into the code. With the above principles, we can have something like this, class Duck(ABC): def __init__(self): pass @abstractmethod def display(self): pass @abstractmethod def swim(self): pass @abstractmethod def quack(self): pass class Flyable(ABC): @abstractmethod def fly(self): pass class RedheadDuck(Duck, Flyable): def swim(self): return "paddling" def quack(self): return "basic quack" def fly(self): return "fly with wings" def display(self): return "redhead duck" # now rubber duck does not need to fly class RubberDuck(Duck): def display(self): return "rubber duck" def swim(self): return "floating" def quack(self): return "jioooo" Now the method is separated away with a class, and for each of the subclass it needs to implement its own fly method, and, of course, for non-flyable type (our ), it does not need to implement the . fly() Flyable rubber duck Flyable So far so good, but troubles never stop. Here comes a new requirement again, we have a bunch of other new ducks, ducks, all of them need to fly and they all fly in the same way. By using our current design, we would have things like this, bluehead, purplehead, yellowhead, … class RedheadDuck(Duck, Flyable): ## omit other methods ... def fly(self): return "fly with wings" def display(self): return "redhead duck" class BlueheadDuck(Duck, Flyable): ... def fly(self): return "fly with wings" def display(self): return "bluehead duck" # ... and many more ducks fly in the same way How does this look to you now? It seems clear that we can reuse the code in the , but however, we are not, but copy-pasting the same code everywhere. fly How do we resolve this problem and make code reusable? Now recall our definition of strategy pattern at the beginning. The Strategy Pattern defines a family of algorithms, encapsulates each one, and makes them interchangeable. Can we have a family of classes that implements the interface and each of them has different fly behaviour which is reusable in the future? Flyable class Flyable(ABC): @abstractmethod def fly(self): pass class Duck(ABC): @abstractmethod def display(self): pass @abstractmethod def swim(self): pass @abstractmethod def quack(self): pass class FlyWithWings(Flyable): def fly(self): return "fly with wings" class FlyWithRockets(Flyable): def fly(self): return "fly with rockets" class RedheadDuck(Duck, FlyWithWings): def swim(self): return "paddling" def quack(self): return "basic quack" def display(self): return "redhead duck" # now objects can REUSE the fly method class BlueheadDuck(Duck, FlyWithRockets): def swim(self): return "paddling" def quack(self): return "basic quack" def display(self): return "blue head duck" Now for every duck that needs to can inherit the FlyWithWings class and with code reusable and without extra burden (each of the subclasses just needs to inherit what is needed). fly with wings It is all good now, and we’ve satisfied most of our requirements and changes should be easy to add into our current design, for example, if we need to introduce a functionalities into some of our duck types, we can add a behaviour interface and introduce of a family of classes implement it, with no overhead to our current system. backflip BackFlip So is inheritance the best for code reuse? What if our classes also have a bunch of functionalities that are redundant for our subclass, inherit the behaviour class would inherit all the redundant. Can we have the flexibility while removing the redundancy? Fly Fly Principle Three Favor composition over inheritance. With composition instead of inheritance, you can encapsulate the methods that you like instead of inheriting all of them. Let’s see how it is implemented ( ) full code class Flyable(ABC): @abstractmethod def fly(self): pass class Duck(ABC): def __init__(self): self.flyable: Flyable = None def performFly(self): return self.flyable.fly() def setFlyBehaviour(self, fly_behaviour: Flyable): self.flyable = fly_behaviour class FlyWithWings(Flyable): def fly(self): return "fly with wings" class FlyWithRockets(Flyable): def fly(self): return "fly with rockets" class RedheadDuck(Duck): def __init__(self): super().__init__() self.flyable = FlyWithWings() def display(self): return "redhead duck" We introduced a variable in the superclass and the actual fly behaviour is implemented in the subclass, self.flyable self.flyable = FlyWithWings() And this part is called , where instead of inheriting, our is composed with the behaviour and this behaviour is able to change at runtime with the method. composition redhead duck FlyWithWings performFly Conclusion What an amazing design we have achieved here! What we have done ( ), full code Separated the method that changes from the original class. Fly Defined a interface and made a collection of classes implements it, each encapsulates different flying behaviour. Flyable Reduce the overhead and redundancy using composition. The strategy pattern really helps in providing flexibility to changes and makes the code reusable. Reference [1] https://github.com/ksatria/MK-Design-Pattern/blob/master/Ebook/Head First Design Patterns.pdf