The Strategy Pattern Explained
Definition
The Strategy pattern defines a family of algorithms, encapsulates each one, and makes them interchangeable. It lets the algorithm vary independently from clients that use it.
Problem: Conditional Logic Explosion
namespace Vendor\Shipping\Model;
class ShippingCalculator
{
public function calculate(string $method, float $weight, float $distance): float
{
return match ($method) {
'flat_rate' => 5.99,
'free' => 0.00,
'weight_based' => $weight * 0.50,
'distance_based' => $distance * 0.10,
'express' => 15.99 + ($weight * 0.30),
'overnight' => 29.99 + ($weight * 0.50) + ($distance * 0.15),
default => throw new \InvalidArgumentException("Unknown method: {$method}")
};
}
}
Problems: Adding a new method modifies this class. Different methods have different parameters. Testing each algorithm requires testing the entire class.
Solution: Strategy Pattern
namespace Vendor\Shipping\Api;
interface ShippingStrategyInterface
{
public function calculate(ShippingRequest $request): float;
public function getCode(): string;
public function getTitle(): string;
}
namespace Vendor\Shipping\Model\Strategy;
class FlatRateStrategy implements \Vendor\Shipping\Api\ShippingStrategyInterface
{
private float $rate = 5.99;
public function calculate(\Vendor\Shipping\Api\ShippingRequest $request): float
{
return $this->rate;
}
public function getCode(): string { return 'flat_rate'; }
public function getTitle(): string { return 'Flat Rate'; }
}
class WeightBasedStrategy implements \Vendor\Shipping\Api\ShippingStrategyInterface
{
private float $pricePerKg = 0.50;
public function calculate(\Vendor\Shipping\Api\ShippingRequest $request): float
{
return $request->getWeight() * $this->pricePerKg;
}
public function getCode(): string { return 'weight_based'; }
public function getTitle(): string { return 'Weight Based'; }
}
class FreeShippingStrategy implements \Vendor\Shipping\Api\ShippingStrategyInterface
{
public function calculate(\Vendor\Shipping\Api\ShippingRequest $request): float
{
return 0.00;
}
public function getCode(): string { return 'free'; }
public function getTitle(): string { return 'Free Shipping'; }
}
// Context: uses strategies interchangeably
class ShippingCalculator
{
private array $strategies = [];
public function __construct(iterable $strategies)
{
foreach ($strategies as $strategy) {
$this->strategies[$strategy->getCode()] = $strategy;
}
}
public function calculate(string $method, ShippingRequest $request): float
{
if (!isset($this->strategies[$method])) {
throw new \InvalidArgumentException("Unknown shipping method: {$method}");
}
return $this->strategies[$method]->calculate($request);
}
}
Adding a new shipping method = creating a new class. Zero modifications to existing code.
Strategy in Magento: Shipping Methods
Magento's Shipping Carrier Architecture
Magento uses the Strategy pattern for shipping carriers. Each carrier implements CarrierInterface:
// Magento\Shipping\Model\Carrier\CarrierInterface
interface CarrierInterface
{
public function collectRates(
\Magento\Shipping\Request $request
): \Magento\Quote\Model\Quote\Address\RateResultInterface;
public function getCode(string $code);
}
// Each carrier is a strategy
class Fedex implements CarrierInterface
{
public function collectRates(\Magento\Shipping\Request $request)
{
// FedEx API call to get real-time rates
$rates = $this->fedexApi->getRates([
'origin' => $request->getOriginZipcode(),
'destination' => $request->getDestZipcode(),
'weight' => $request->getPackageWeight(),
]);
return $this->buildResult($rates);
}
}
class FreeShipping implements CarrierInterface
{
public function collectRates(\Magento\Shipping\Request $request)
{
$result = $this->rateFactory->create();
$result->append($this->createFreeMethod());
return $result;
}
}
// The carrier code is selected at runtime based on configuration
class ShippingConfig
{
public function getActiveCarriers(): array
{
return $this->config->getValue('carriers');
}
}
The active shipping method is determined by configuration, and the corresponding strategy (carrier) handles rate calculation.
Strategy in Magento: Payment Processing
Payment Method Strategies
// Each payment method is a strategy
interface PaymentMethodInterface
{
public function authorize(
\Magento\Payment\Model\InfoInterface $payment,
float $amount
): bool;
public function capture(
\Magento\Payment\Model\InfoInterface $payment,
float $amount
): bool;
public function refund(
\Magento\Payment\Model\InfoInterface $payment,
float $amount
): bool;
}
class CreditCardMethod implements PaymentMethodInterface
{
public function authorize(\Magento\Payment\Model\InfoInterface $payment, float $amount): bool
{
return $this->gateway->authorize($payment->getData('cc_number'), $amount);
}
public function capture(\Magento\Payment\Model\InfoInterface $payment, float $amount): bool
{
return $this->gateway->capture($payment->getTransactionId(), $amount);
}
}
class BankTransferMethod implements PaymentMethodInterface
{
public function authorize(\Magento\Payment\Model\InfoInterface $payment, float $amount): bool
{
// No real-time authorization — pending until bank confirms
return true;
}
public function capture(\Magento\Payment\Model\InfoInterface $payment, float $amount): bool
{
// Capture happens when bank transfer is confirmed
return $this->bankApi->checkTransferStatus($payment->getTransactionId());
}
}
// Context: Payment processor uses the selected strategy
class PaymentProcessor
{
public function process(
PaymentMethodInterface $method,
\Magento\Payment\Model\InfoInterface $payment,
float $amount
): bool {
if (!$method->authorize($payment, $amount)) {
return false;
}
return $method->capture($payment, $amount);
}
}
The payment method is selected at checkout based on store configuration. The PaymentProcessor doesn't care which strategy handles the payment.
Quiz
1. The Strategy pattern is best used when:
2. How does the Strategy pattern differ from a switch statement?
3. In Magento, shipping carriers are an example of:
Flashcards
Question
What does the Strategy pattern do?
Click to reveal answer
Answer
Defines a family of algorithms, encapsulates each, and makes them interchangeable
Question
Strategy vs switch statement?
Click to reveal answer
Answer
Strategy: each algorithm in its own class (OCP). Switch: all logic in one place.
Question
Magento shipping carriers use which pattern?
Click to reveal answer
Answer
Strategy pattern — each carrier implements CarrierInterface differently
Revision Notes
Key Takeaways
- 1. Strategy defines a family of algorithms behind a common interface
- 2. Each strategy is a separate class, making adding new algorithms easy
- 3. Context selects and uses strategies at runtime
- 4. Magento uses Strategy for shipping carriers, payment methods, tax calculators
- 5. Strategy follows OCP: new algorithms = new classes, no modifications
Interview Tips
- • Give Magento examples: shipping carriers, payment methods, tax calculators
- • Explain why Strategy beats switch: OCP compliance, testability, separation
- • Discuss when Strategy is overkill (2-3 simple algorithms)
Cheat Sheet
Strategy Pattern:
Interface → ShippingStrategyInterface
Strategies → FlatRate, WeightBased, FreeShipping, Express
Context → ShippingCalculator (selects and uses strategy)
Magento Examples:
Shipping: CarrierInterface (FedEx, UPS, DHL)
Payment: PaymentMethodInterface (CreditCard, PayPal)
Tax: TaxCalculatorInterface (VAT, GST, SalesTax)
When to use: multiple algorithms, runtime selection, need to add new ones