In simple terms, Strategy means different ways of doing the same thing. For example, there may be different routes to the same destination. In fact when google maps suggests an alternative route due to traffic, it is building an alternative strategy for us to achieve the end result. In this case reaching our destination.
Lets look at an example. Assume we have some kind of data and want to store. Our end goal is to store the data eventually but there are different ways of storing the data. We can store it in the Session or in a Relationsl Database or in a No SQL Database. There are different strategies for us to pick from. This sort of situation makes it a good candidate for using a Strategy Pattern. Lets look at some code now.
We will follow SOLID Principles, therefore code to an interface allowing for extension but close it for modification.
interface StorageStrategy
{
public function store($data);
}
Now, let's go ahead and create our first strategy, which will be to store the data in the Session Storage. The Session Storage Class will implement the StorageStrategy. This means we are signing a contract agreeing to implement the store method no matter what. Also the store method will exactly accept one parameter called data like so, store($data).
class SessionStorage implements StorageStrategy
{
public function store($data): string
{
return "Storing {$data} in Session";
}
}
We have created our first strategy that has a store method and the store method accepts exactly one parameter defined by our StorageStrategy contract.
Let's do one more strategy, this time we will support RelationalDatabaseStorage.
class RelationalDatabaseStorage implements StorageStrategy
{
public function store($data): string
{
return "Storing {$data} in Relational Database";
}
}
Now that we have our 2 strategies, let's see how to use them.
class DataStore
{
protected $data;
public function __construct($data)
{
$this->data = $data;
}
public function store(StorageStrategy $storage): string
{
return $storage->store($this->data);
}
}
The above code is our client that has a Constructor that accepts the data. We then have the Store method that accepts our Strategy contract. Instead of acceptinng the Strategy object. This is exactly what allows us to easily switch between our strategies. We can pass any Strategy object as long as they have signed the StorageStrategy contract.
We can then call the store($data) method on the Strategy contract that will allow us to have our own implementation of what needs to happen in the store method.
$datastore = new DataStore("{'data' : 'some data'}");
echo $datastore->store(new SessionStorage);
echo $datastore->store(new RelationalDatabaseStorage);
// Output: Storing {'data' : 'some data'} in Session
// Output: Storing {'data' : 'some data'} in Relational Database
We talked about how easy it would be for extension, let's say that our data volume has increased over time drastically and our previous strategies aren't working anymore and we want to now switch to a NoSqlDBStorage strategy.
class NoSqlDBStorage implements StorageStrategy
{
public function store($data): string
{
return "Storing {$data} in NoSqlDBStorage";
}
}
Let's see how our client code looks like to adopt out new strategy.
$datastore = new DataStore("{'data' : 'some data'}");
echo $datastore->store(new NoSqlDBStorage);
// Output: Storing {'data' : 'some data'} in NoSqlDBStorage
It's as simple as that. All we had to do is create a new NoSqlDBStorage strategy and swap it out.
That's it you now know what a strategy pattern is. Hope you find this article useful 👍.