Strategy Design Pattern
Hi everyone, I have been checking out design patterns recently and thought it would be cool to share some of them with you. Design patterns are common solutions to recurring problems in software development. They help us write more maintainable, reusable, and flexible code.
The first pattern I want to talk about is the STRATEGY DESIGN PATTERN. The official definition is
“The Strategy Pattern defines a family of algorithms, encapsulates each one, and makes them interchangeable. Strategy lets the algorithm vary independently from clients that use it”.
Let’s break down what that means.
- A family of algorithms means that we have a set of different ways to achieve the same goal, such as sorting a list, compressing a file, or validating an input. Each algorithm has its advantages and disadvantages, depending on the context and the requirements.
- Encapsulating each one means that we hide the implementation details of each algorithm behind a common interface or an abstract class. This way, we can swap one algorithm for another without changing the code that uses it. Making them interchangeable means that we can choose which algorithm to use at runtime, depending on some criteria or user input. For example, we can let the user choose how they want to sort a list, or we can decide which compression algorithm to use based on the file size.
- Strategy lets the algorithm vary independently from clients that use it means that we decouple the logic of the algorithm from the logic of the client. The client only knows about the interface or the abstract class and does not care about how the algorithm is implemented.
This makes our code more modular and easier to test. To illustrate this pattern, let’s look at an example where we want to create a PaymentProvider class that can handle different payment methods, such as card or crypto. We can define an interface called PaymentStrategy that declares a method called pay(). Then, we can create concrete classes that implement this interface, such as CardPaymentStrategy and CryptoPaymentStrategy. Each class will have its logic for processing the payment.
Next, we can create a PaymentProvider class that has a private field of type PaymentStrategy. This field will hold a reference to the current payment strategy that is being used. The PaymentProvider class will also have a constructor that takes a PaymentStrategy as an argument, and a method called processPayment() that delegates the payment to the current strategy.
Finally, we can create different instances of PaymentProvider with different strategies, and use them to process payments. We can also change the strategy at runtime by using a setter method.
Here is how the code would look like in PHP:
<?php
// Define an interface for payment strategies
interface PaymentStrategy {
public function pay($amount);
}// Define a concrete class for card payment strategy
class CardPaymentStrategy implements PaymentStrategy {
public function pay($amount) {
// Logic for paying with card
echo "Paying $amount with card\n";
}
}// Define a concrete class for crypto payment strategy
class CryptoPaymentStrategy implements PaymentStrategy {
public function pay($amount) {
// Logic for paying with crypto
echo "Paying $amount with crypto\n";
}
}// Define a class for payment provider
class PaymentProvider {
private $paymentStrategy; // A reference to the current payment strategy // Constructor that takes a payment strategy as an argument
public function __construct(PaymentStrategy $paymentStrategy) {
$this->paymentStrategy = $paymentStrategy;
} // A method that delegates the payment to the current strategy
public function processPayment($amount) {
$this->paymentStrategy->pay($amount);
} // A setter method that allows changing the payment strategy at runtime
public function setPaymentStrategy(PaymentStrategy $paymentStrategy) {
$this->paymentStrategy = $paymentStrategy;
}
}// Create different instances of payment provider with different strategies
$cardProvider = new PaymentProvider(new CardPaymentStrategy());
$cryptoProvider = new PaymentProvider(new CryptoPaymentStrategy());// Use them to process payments
$cardProvider->processPayment(100); // Paying 100 with card
$cryptoProvider->processPayment(200); // Paying 200 with crypto// Change the strategy at runtime
$cardProvider->setPaymentStrategy(new CryptoPaymentStrategy());
$cardProvider->processPayment(300); // Paying 300 with crypto?>
I hope this example helps you understand how the strategy design pattern works and how it can be useful in your projects. In the next post, I will talk about another design pattern called the observer design pattern. Stay tuned!