Usage examples: The Strategy pattern is very common in Python code. It’s often used in various frameworks to provide users a way to change the behavior of a class without extending it.
Identification: Strategy pattern can be recognized by a method that lets a nested object do the actual work, as well as a setter that allows replacing that object with a different one.
Conceptual Example
This example illustrates the structure of the Strategy design pattern. It focuses on answering these questions:
What classes does it consist of?
What roles do these classes play?
In what way the elements of the pattern are related?
main.py: Conceptual example
from__future__import annotationsfrom abc import ABC, abstractmethodfrom typing import ListclassContext():""" The Context defines the interface of interest to clients. """def__init__(self,strategy: Strategy) ->None:""" Usually, the Context accepts a strategy through the constructor, but also provides a setter to change it at runtime. """ self._strategy = strategy@propertydefstrategy(self) -> Strategy:""" The Context maintains a reference to one of the Strategy objects. The Context does not know the concrete class of a strategy. It should work with all strategies via the Strategy interface. """return self._strategy@strategy.setterdefstrategy(self,strategy: Strategy) ->None:""" Usually, the Context allows replacing a Strategy object at runtime. """ self._strategy = strategydefdo_some_business_logic(self) ->None:""" The Context delegates some work to the Strategy object instead of implementing multiple versions of the algorithm on its own. """# ...print("Context: Sorting data using the strategy (not sure how it'll do it)") result = self._strategy.do_algorithm(["a", "b", "c", "d", "e"])print(",".join(result))# ...classStrategy(ABC):""" The Strategy interface declares operations common to all supported versions of some algorithm. The Context uses this interface to call the algorithm defined by Concrete Strategies. """@abstractmethoddefdo_algorithm(self,data: List):pass"""Concrete Strategies implement the algorithm while following the base Strategyinterface. The interface makes them interchangeable in the Context."""classConcreteStrategyA(Strategy):defdo_algorithm(self,data: List) -> List:returnsorted(data)classConcreteStrategyB(Strategy):defdo_algorithm(self,data: List) -> List:returnreversed(sorted(data))if__name__=="__main__":# The client code picks a concrete strategy and passes it to the context.# The client should be aware of the differences between strategies in order# to make the right choice. context =Context(ConcreteStrategyA())print("Client: Strategy is set to normal sorting.") context.do_some_business_logic()print()print("Client: Strategy is set to reverse sorting.") context.strategy =ConcreteStrategyB() context.do_some_business_logic()
Output.txt: Execution result
Client: Strategy is set to normal sorting.
Context: Sorting data using the strategy (not sure how it'll do it)
a,b,c,d,e
Client: Strategy is set to reverse sorting.
Context: Sorting data using the strategy (not sure how it'll do it)
e,d,c,b,a