Summary
Pattern Name | Strategy Pattern |
Pattern Type | Behavioral Pattern |
Other Names | Policy |
Scope | Object |
Tagline | Use an algorithm from a group of interchangeable algorithms |
Use cases | Wherever there are multiple algorithms to serve a similar purpose |
Related Patterns | Factory Decorator |
Difficulty Level | Medium |
Implementations |
Definition
When there is a group of similar algorithms and we need a pattern to decide among those patterns, then we can use Strategy pattern.
Using Strategy pattern we can decide which algorithm to use, on the fly. The existing classes are not changed while implementing Strategy pattern, a new Strategy class is introduced, which handles the operations.
Strategy pattern makes the algorithms interchangeable and lets them vary independently from client to client.
The client must be aware of the differences between different Strategy(algorithm) objects. Without knowing the differences the client can not decide when to use which algorithm.
If we want to add/introduce a new algorithm to the system, then we just need to create that algorithm class and implement the strategy interface for the class. No other change will be required. So introducing new algorithms becomes very easy.
Use Cases
Here are a few use cases of the Strategy pattern-
- When there are multiple related classes (which differ in their behavior), and we need to use any one of them in certain cases.
- When we need multiple variations of the same algorithm and we want to decide their usage at run-time.
- When we want to avoid specifying an algorithm usage strictly at a certain case.
Implementation
Strategy pattern has 3 elements:
- Item Algorithm Interface: interface to make sure the same type for all Item Concrete classes (algorithm classes).
- Item Concrete Classes (algorithm classes): algorithm classes implementing the Item Interface.
- Strategy Class: class that takes the algorithm instance(in the constructor), and then uses that algorithm for performing operations on that algorithm classes. This strategy class uses/calls the functions from the algorithm class (may include some additional operations).
Use the following steps and criteria to implement Strategy pattern.
- Make sure the different classes implement the same interface and are supposed to be used for the same purpose.
- Create a class to implement the strategy process.
- The constructor of Strategy class takes parameters to know which one of the classes, the user wants to use.
- Add any required functions in the strategy class, and use the functions from the actual implementations. As the reference of the actual implementation class is stored in the strategy class, so that reference can be used to call functions from those actual classes.
- While using the Strategy class, pass an instance of the actual that you want to use. Like, new StrategyClass(new ActualStrategyClass1()).
- The client uses functions from the strategy class and does not use functions from actual/algorithm classes directly.
The Strategy class does not need to use all the functions from the actual classes/algorithms. Only the required functions will be used.
In the implementation(& usage), Strategy pattern creates two objects – one is of the Strategy Class and another is off the Algorithm class. This keeps their concerns separate.
Key points in the implementation are:
- The item concrete classes (algorithm classes) implement the same interface.
- The strategy class holds a reference of the algorithm class that will be used. That reference is used to execute functions from that algorithm class(item class).
Examples
Example #1: Storage Selection
Let’s consider file storage functionality in an application. Take a look at the class diagram first:
Here we have different ways to store files – Local storage, Google Drive and S3. We will use the Strategy pattern (using StorageStrategy class) to use any of the available storage methods when we need any of these.
Storage Interface
interface Storage
function storeFile(tempPath): Integer
retrieveFile(fileId): String
printFileInfo(fileId)
end interface
Local Storage Class
class LocalStorage implements Storage
function storeFile(String tempPath): Integer
// Code to store file here
// return the ID that is obtained from the database record or any other unique identifier.
return fileId;
end function
function retrieveFile(fileId): String
// Retrieve the url and return back
// some dummy url returned for demo
return "https://bigboxcode.com/files/local/" + fileId
end function
function printFileInfo(fileId)
print("Storage type: Local Storage")
print("File ID: " + fileId)
print("File URL: " + retrieveFile(fileId))
end function
end class
Google Drive Storage Class
class GoogleDriveStorage implements Storage
function storeFile(tempPath): Integer
// Code to store file here
// return the ID that is obtained from the database record or any other unique identifier.
return fileId
end function
function retrieveFile(fileId): String
// Retrieve the url and return back
// Some dummy url is returned for demo purpose
return "https://drive.google.com/file/d/1234_9K7654hu6RT_9j7JKY3fK/view"
end function
function printFileInfo(fileId)
print("Storage type: Google Drive")
print("File ID: " + fileId)
print("File URL: " + retrieveFile(fileId))
end function
end class
AWS S3 Storage Class
class S3Storage implements Storage
function storeFile(tempPath): Integer
// Code to store file here
// return the ID that is obtained from the database record or any other unique identifier.
return fileId
end function
function retrieveFile(fileId): String
// Retrieve the url and return back
// Some dummy url is returned for demo purpose
return "https://bigboxcode.s3.amazonaws.com/pdf/UC-0e7654338-5697-4f99-b33-d89h87g5gf4gwfg.pdf"
end function
function printFileInfo(int fileId)
print("Storage type: AWS S3")
print("File ID: " + fileId)
print("File URL: " + retrieveFile(fileId))
end function
end class
Storage Strategy Class
class StorageStrategy
var storage: Strategy
constructor(storageTypeParam: Storage) {
storage = storageTypeParam
end constructor
function uploadFile(tempPath): Integer
int fileId = storage.storeFile(tempPath)
storage.printFileInfo(fileId)
return fileId
end function
function getFileUrl(fileId): Integer
return storage.retrieveFile(fileId)
end function
end class
Demo
// Use Local Storage
fileStorage: StorageStrategy = new StorageStrategy(new LocalStorage())
fileStorage.uploadFile("/some-temp-path")
// Use S3
fileStorage = new StorageStrategy(new S3Storage())
fileStorage.uploadFile("/some-temp-path")
// Use Google drive
fileStorage = new StorageStrategy(new GoogleDriveStorage())
fileStorage.uploadFile("/some-temp-path")
Code Implementations
Use the following links to check Strategy pattern implementation in specific programming languages.