🧩 Strategy Pattern
✅ Intent
- Separate behavior based on conditions into individual strategies
- Allow the strategy to be selected externally for flexible switching
✅ Motivation
- Branching logic is divided into independent objects, making it easier to test and extend
- Ideal for systems where algorithm swapping is required
✅ When to Use
- When algorithms or processing logic need to be dynamically switched
- When behaviors can be clearly categorized, such as "Pattern A", "Pattern B", etc.
✅ Code Example
- TypeScript
- PHP
- Python
// Strategy インターフェース
interface DiscountStrategy {
calculate(basePrice: number): number;
}
// ConcreteStrategy: 学生割引
class StudentDiscount implements DiscountStrategy {
calculate(basePrice: number): number {
return basePrice * 0.8;
}
}
// ConcreteStrategy: 会員割引
class MemberDiscount implements DiscountStrategy {
calculate(basePrice: number): number {
return basePrice * 0.9;
}
}
// ConcreteStrategy: VIP割引
class VIPDiscount implements DiscountStrategy {
calculate(basePrice: number): number {
return basePrice * 0.7;
}
}
// ConcreteStrategy: 割引なし
class NoDiscount implements DiscountStrategy {
calculate(basePrice: number): number {
return basePrice;
}
}
// Context クラス
class PriceCalculator {
constructor(private discountStrategy: DiscountStrategy) {}
calculatePrice(basePrice: number): number {
return this.discountStrategy.calculate(basePrice);
}
}
// 利用例
const basePrice = 1000;
const studentCalculator = new PriceCalculator(new StudentDiscount());
console.log(studentCalculator.calculatePrice(basePrice)); // 800
const memberCalculator = new PriceCalculator(new MemberDiscount());
console.log(memberCalculator.calculatePrice(basePrice)); // 900
const vipCalculator = new PriceCalculator(new VIPDiscount());
console.log(vipCalculator.calculatePrice(basePrice)); // 700
const regularCalculator = new PriceCalculator(new NoDiscount());
console.log(regularCalculator.calculatePrice(basePrice)); // 1000
<?php
// Strategy インターフェース
interface DiscountStrategy {
public function calculate(float $basePrice): float;
}
// ConcreteStrategy: 学生割引
class StudentDiscount implements DiscountStrategy {
public function calculate(float $basePrice): float {
return $basePrice * 0.8;
}
}
// ConcreteStrategy: 会員割引
class MemberDiscount implements DiscountStrategy {
public function calculate(float $basePrice): float {
return $basePrice * 0.9;
}
}
// ConcreteStrategy: VIP割引
class VIPDiscount implements DiscountStrategy {
public function calculate(float $basePrice): float {
return $basePrice * 0.7;
}
}
// ConcreteStrategy: 割引なし
class NoDiscount implements DiscountStrategy {
public function calculate(float $basePrice): float {
return $basePrice;
}
}
// Context クラス
class PriceCalculator {
private DiscountStrategy $discountStrategy;
public function __construct(DiscountStrategy $discountStrategy) {
$this->discountStrategy = $discountStrategy;
}
public function calculatePrice(float $basePrice): float {
return $this->discountStrategy->calculate($basePrice);
}
}
// 利用例
$basePrice = 1000;
$studentCalculator = new PriceCalculator(new StudentDiscount());
echo $studentCalculator->calculatePrice($basePrice) . PHP_EOL; // 800
$memberCalculator = new PriceCalculator(new MemberDiscount());
echo $memberCalculator->calculatePrice($basePrice) . PHP_EOL; // 900
$vipCalculator = new PriceCalculator(new VIPDiscount());
echo $vipCalculator->calculatePrice($basePrice) . PHP_EOL; // 700
$regularCalculator = new PriceCalculator(new NoDiscount());
echo $regularCalculator->calculatePrice($basePrice) . PHP_EOL; // 1000
from abc import ABC, abstractmethod
# Strategy インターフェース
class DiscountStrategy(ABC):
@abstractmethod
def calculate(self, base_price: float) -> float:
pass
# ConcreteStrategy: 学生割引
class StudentDiscount(DiscountStrategy):
def calculate(self, base_price: float) -> float:
return base_price * 0.8
# ConcreteStrategy: 会員割引
class MemberDiscount(DiscountStrategy):
def calculate(self, base_price: float) -> float:
return base_price * 0.9
# ConcreteStrategy: VIP割引
class VIPDiscount(DiscountStrategy):
def calculate(self, base_price: float) -> float:
return base_price * 0.7
# ConcreteStrategy: 割引なし
class NoDiscount(DiscountStrategy):
def calculate(self, base_price: float) -> float:
return base_price
# Context クラス
class PriceCalculator:
def __init__(self, discount_strategy: DiscountStrategy):
self._discount_strategy = discount_strategy
def calculate_price(self, base_price: float) -> float:
return self._discount_strategy.calculate(base_price)
# 利用例
base_price = 1000
student_calculator = PriceCalculator(StudentDiscount())
print(student_calculator.calculate_price(base_price)) # 800
member_calculator = PriceCalculator(MemberDiscount())
print(member_calculator.calculate_price(base_price)) # 900
vip_calculator = PriceCalculator(VIPDiscount())
print(vip_calculator.calculate_price(base_price)) # 700
regular_calculator = PriceCalculator(NoDiscount())
print(regular_calculator.calculate_price(base_price)) # 1000
✅ Explanation
This code applies the Strategy
pattern to enable flexible switching between different discount calculation logics.
The Strategy
pattern encapsulates algorithms or logic into separate classes, allowing them to be swapped dynamically.
1. Overview of the Strategy Pattern
-
Strategy: Defines a common interface to treat different algorithms uniformly
- Represented in this code by
DiscountStrategy
- Represented in this code by
-
ConcreteStrategy: Implements the strategy interface and provides specific algorithms
- Implemented by
StudentDiscount
,MemberDiscount
,VIPDiscount
, andNoDiscount
in this code
- Implemented by
-
Context: Uses a
Strategy
and switches algorithms dynamically- Represented by
PriceCalculator
in this code
- Represented by
2. Key Classes and Their Roles
-
DiscountStrategy
- The common interface for discount calculations
- Defines the method
calculate(basePrice: number): number
-
StudentDiscount
,MemberDiscount
,VIPDiscount
,NoDiscount
- Concrete strategy classes that implement
DiscountStrategy
- Each class applies a different discount rate
- Concrete strategy classes that implement
-
PriceCalculator
- The context class
- Receives a
DiscountStrategy
instance via its constructor and calculates the final price using thecalculatePrice
method
3. UML Class Diagram
4. Benefits of the Strategy Pattern
- Flexibility: New discount logics can be added by simply implementing
DiscountStrategy
- Single Responsibility Principle: Each discount logic is isolated in its own class, improving maintainability
- Runtime Switching: Different discount strategies can be swapped easily at runtime
This design is highly effective in cases where algorithm switching is needed and significantly improves code extensibility and maintainability.