The Strategy Design Pattern is a behavioral design pattern that enables selecting an algorithm's behavior at runtime.
Instead of implementing multiple variations of an algorithm in a single class, it allows you to define a family of algorithms, encapsulate each in its own class, and make them interchangeable.
Components of the Strategy Pattern (UML):
-
Context Class: Maintains a reference to a Strategy object and interacts with it through a common interface.
- Example: The User class interacts with different strategies for Quality and AdPolicy.
-
Strategy Interface: Defines a common interface for all concrete strategies.
- Example: Quality and ADPolicies are abstract interfaces defining behavior.
-
Concrete Strategies: Implement the Strategy interface with specific algorithms.
- Example: FreeUserQuality, PremiumUserQuality, BasicUserQuality, FreeUserAdPolicy, etc.
When to Use the Strategy Pattern
Use the Strategy Pattern when the benefits of flexibility and maintainability outweigh the overhead of managing multiple strategy classes.
-
Many Algorithms:
- When you need to define multiple variations of an algorithm or behavior within a single class.
- Example: Defining video quality for different subscription plans (Free, Basic, Premium).
-
Runtime Decisions:
- When the behavior of a class needs to change dynamically based on user input or other runtime conditions.
- Example: Selecting compression algorithms (ZIP, RAR, 7z) at runtime.
-
Avoid Excessive Use of if or switch Statements:
- Replace conditional logic with interchangeable strategy classes.
- Example: Payment processing (Credit Card, PayPal, UPI) without a massive if-else block.
-
Encapsulation of Variations:
- Encapsulate algorithm variations in separate classes to keep the context class clean.
- Example: Logging strategies (ConsoleLogger, FileLogger, RemoteLogger).
-
Open/Closed Principle:
- Ensure the system is open for extension but closed for modification by adding new strategies without altering existing code.
- Example: Adding a new user type (EnterpriseUserQuality) in the example system.
Example:
from abc import ABC, abstractmethod
from enum import Enum
# Enum for User Types
class UserType(Enum):
FREE = 0
BASIC = 1
PREMIUM = 2
# Strategy Interface for Quality
class Quality(ABC):
@abstractmethod
def get_quality(self):
pass
# Strategy Interface for Ad Policy
class AdPolicy(ABC):
@abstractmethod
def has_ads(self):
pass
# Concrete Strategy for Quality
class FreeUserQuality(Quality):
def get_quality(self):
return ['SD']
class BasicUserQuality(Quality):
def get_quality(self):
return ['SD', 'HD']
class PremiumUserQuality(Quality):
def get_quality(self):
return ['SD', 'HD', 'UHD']
# Concrete Strategy for Ad Policy
class FreeUserAdPolicy(AdPolicy):
def has_ads(self):
return True
class BasicUserAdPolicy(AdPolicy):
def has_ads(self):
return True
class PremiumUserAdPolicy(AdPolicy):
def has_ads(self):
return False
# Context Class
class User:
def __init__(self, user_type: UserType, quality: Quality, ad_policy: AdPolicy):
self.user_type = user_type
self.quality = quality
self.ad_policy = ad_policy
def get_quality(self):
return self.quality.get_quality()
def has_ads(self):
return self.ad_policy.has_ads()
# Usage
free_user = User(UserType.FREE, FreeUserQuality(), FreeUserAdPolicy())
basic_user = User(UserType.BASIC, BasicUserQuality(), BasicUserAdPolicy())
premium_user = User(UserType.PREMIUM, PremiumUserQuality(), PremiumUserAdPolicy())
print("Free User Quality:", free_user.get_quality()) # ['SD']
print("Free User Ads:", free_user.has_ads()) # True
print("Premium User Quality:", premium_user.get_quality()) # ['SD', 'HD', 'UHD']
print("Premium User Ads:", premium_user.has_ads()) # False
Advantages of the Strategy Pattern:
- Flexibility: Algorithms can be swapped at runtime without altering the context class.
- Extensibility: New strategies can be added without modifying existing code.
- Readability and Maintenance: Reduces clutter in the main class by delegating logic to specific strategy classes.
- Adherence to SOLID Principles: Particularly supports the Open/Closed Principle.
Disadvantages of the Strategy Pattern:
- Increased Complexity: Introduces additional classes and objects to manage.
- Overhead: If the number of strategies is small or infrequently changed, the pattern might add unnecessary complexity.
Additional Examples
- Sorting Algorithms: Using different sorting strategies (e.g., QuickSort, MergeSort, BubbleSort) dynamically.
- Discount Calculation: Applying different discount strategies (FlatDiscount, PercentageDiscount) based on user type.
- Authentication Mechanisms: Switching between different authentication methods (OAuth, JWT, BasicAuth).
Top comments (0)