Design Patterns - Strategy Pattern
Strategy Pattern is a software design pattern that defines a family of algorithms and encapsulates them, allowing the programmer to assign the algorithm at run time instead of hard coding it in the class definition.
As an avid reader one of the problems one faces is forgetting what they have read in the past. There are several ways to deal with that and a discussion on that would require a post of its own. One of the ways I thought of was creating my own personal notes and summaries of what I read as a means of documenting for my own future reference. Initially, this meant setting up a TiddlyWiki instance and creating a personal knowledgebase. But I later realized that curating a blog would serve both as a means of motivation as well as helping out others in the same boat.
The inspiration of this series of posts on design patterns is Head First Design Patterns Second Edition. Head First is a series of books published by O'Reilly Media that uses a non conventional mode of teaching. Instead of terse text that would put you to sleep the Head First series employs comics to make their books a fun read. Eric Freeman, Elisabeth Freeman, Kathy Sierra and Bert Bates came out with their latest edition of Head First Design Patterns, which I have been following on Safari through their early release program.
Since I do not wish to infringe on the rights of the authors I will be writing this post in my own words and also use my own code examples. This series would be my own impressions and understanding of the subject matter. I highly recommend checking out the book both to read about it more comprehensively and as a way to support the authors. I hope to make this either a weekly or bi-weekly series with posts coming out every Monday. Digressing from the topic, bi-weekly is a pretty confusing word, it can imply both twice a week or every two weeks though my intention is the latter.
Design Patterns
Coming to the crux of this post Design Patterns - originally proposed by Christopher Alexander, emeritus professor at the University of California, Berkeley, design patterns are a way of creating reusable solutions, writing extensible and maintainable codebases. These patterns soared in popularity after the release of the book Design Patterns: Elements of Reusable Object-Oriented Software colloquially known as the book by "Gang of Four".
Design patterns represent and show the relationship between classes and objects without proposing actual concrete code. In simpler terms they are usually a representation of best practices to be followed.
Strategy Pattern
The first pattern I would like to talk about is the strategy pattern which forms the basis of quite a few other design patterns. The text book definition of this pattern would be
Strategy Pattern is a software design pattern that defines a family of algorithms and encapsulates them, allowing the programmer to assign the algorithm at run time instead of hard coding it in the class definition.
What this means for the programmer is that, when designing your program identify the parts of the program that may vary and encapsulate them so you can modify or extend their behaviour without affecting the rest of your codebase.
So how does this work in practice? Let's take an example of a program that with cars!
//Car.java
public abstract class Car{
public abstract void PoweredBy();
public abstract void DriveMode();
public Car(){}
}
//Tesla.java
public class Tesla extends Car{
public void PoweredBy(){
System.out.println("This car is powered by electricity!");
}
public DriveMode(){
System.out.println("This car driven automatically!");
}
}
//Toyota.java
public class Toyota extends Car{
public void PoweredBy(){
System.out.println("This car is powered by gasoline!");
}
public void DriveMode(){
System.out.println("This car driven manually!");
}
}
In this example we create a parent class Car
from which all the child classes are inherited and each overrides the parent's methods. Say we were building a game that lets us upgrade and modify the vehicles that we use, perhaps the game would allow the user to replace the internal combustion engine with an electric one. This would require a change in the class methods at run time. Brute force would suggest creating every possible combination and switching them as needed but this would be incredibly cumbersome and for those who have read the Pragmatic Programmer, an utter violation of the principles of DRY (Don't repeat yourself). Therefore, it's pertinent that something needs to be done about PoweredBy
and DriveMode
both of which fall under the category of things that can change and need to be mutable.
The solution to this would be to programming to an interface instead of a concrete implementation. An interface in object oriented programming languages is a contract that guarantees to a client how a class or structure will behave. What this means is, all encapsulated methods will have the same guarantees and method prototypes that we will require in our Car
class.
//PoweredByBehaviour.java
public interface PoweredByBehaviour{
public void PoweredBy();
}
//DriveBehaviour.java
public interface DriveBehaviour{
public void DriveMode();
}
Now we can build classes to implement the above behaviours. This would enforce all classes implementing the above interfaces would follow the rules defined by the interface. Furthermore, due to programming to an interface we will be able change the way we instantiate a class and we also use getters and setters to manage the behaviour at run time.
//ElectricPower.java
public class ElectricPower implements PoweredByBehaviour{
public void PoweredBy(){
System.out.println("This car is powered by electricity!");
}
}
//GasolinePowered.java
public class GasolinePowered implements PoweredByBehaviour{
public void PoweredBy(){
System.out.println("This car is powered by gasoline!");
}
}
//ManualDrive.java
public class ManualDrive implements PoweredByBehaviour{
public void DriveMode(){
System.out.println("This car driven manually!");
}
}
//AutomaticDrive.java
public class AutomaticDrive implements PoweredByBehaviour{
public void DriveMode(){
System.out.println("This car driven automatically!");
}
}
//How to instantiate the above classes
DriveBehaviour driveBehaviour = new ManualDrive();
So now what does our final class look like?
//Car.java
public abstract class Car{
PoweredByBehaviour poweredByBehaviour;
DriveBehaviour driveBehaviour;
public Car(){}
public void PerformPoweredBy(){
poweredByBehaviour.PoweredBy();
}
public void PerformDriveMode(){
driveBehaviour.DriveMode();
}
public void setPoweredBy(PoweredByBehaviour pb){
poweredByBehaviour = pb;
}
public void setDriveMode(DriveBehaviour db){
driveBehaviour = db;
}
}
//Tesla.java
public class Tesla extends Car{
public Tesla(){
poweredByBehaviour = new ElectricPower();
driveBehaviour = new AutomaticDrive();
}
}
//Toyota.java
public class Toyota extends Car{
public Toyota(){
poweredByBehaviour = new GasolinePowered();
driveBehaviour = new ManualDrive();
}
}
As you can see, there's the main class from which all sub classes inherit has several improvements. Due to behaviours being implementations of interfaces defined above they can be switched as needed. The Tesla
and Toyota
classes come with their default implementations of behaviours but the addition of set methods means that we simply can switch their behaviour as needed at run time. We have managed to avoid duplication of code and every behaviour enjoys a single definition which can be altered at once.
Closing Thoughts
So that's it! This should give you a brief idea of what design patterns can do. This is the first post that I have written so far, hopefully the first of many more such posts. I started writing these use my time during the Coronavirus lockdowns productively. I love would to hear your feedback on this post, feel free to write to me at akshay [at] akshayprabhu [dot] dev or tweet at my twitter handle @akshaprabhu200.