rethink the Strategy, Command, Template Method, and Visitor patterns.
General idea: you can replace instances of some participant class in these patterns with simple functions, reducing a lot of boilerplate code.
Case Study: Refactoring Strategy
Strategy is a good example of a design pattern that can be simpler in Python if you leverage functions as firs-class objects.
we will describe and implement Strategy using the classic structure described in Design Patterns.
Classic Strategy
acc to Design Patterns, Strategy pattern is –
- define a family of algorithms, encapsulate each one, and make them interchangeable.
- Strategy lets the algorithm vary independently from clients that use it.
e.g. compute discounts to orders according to the attributes of the customer or inspection of the ordered items.
—.
Consider an online store with these discount rules:
- customers with 1,000 or more fidelity points get a global 5% discount per order
- a 10% discount is applied to each line item with 20 or more units in the same order
- orders with at least 10 distinct items get a 7% global discount
Context:
- provides a service by delegating some computation to interchangeable components that implement alternative algorithms
- (in this example, the context is an
Order
, which is configured to apply a promotional discount according to one of several algorithms)Strategy:
- the interface common to the components that implement the different algoriths.
- (in this example, this role is played by an abstract class called
Promotion
)Concrete Strategy
- one of the concrete subclasses of Strategy
(in this example,FidelityPromo
,BulkitemPromo
,LargeOrderPromo
)
implementation
the concrete strategy is chosen by the client of the context class.
(before instantiating an order, the system would somehow select a promotional discount strategy and pass it to theOrder
constructor. selection of strategy is out of scope)
Demonstration
Function-Oriented Strategy
Choosing the Best Strategy: Simply Approach
Finding Strategies in a Module
Modules in Python are also first-class objects, and the standard library provides several functions to handle them.
The built-in globals
is described as follows in the doc:
return a dictionary representing the current global symbol table.
This is always the dictionary of the current module (inside a function or method, this is the module where it is defined, not the module from which it is called)
Command
Command is another design pattern that can be simplified by the use of functions passed as arguments.
The goal of Command is to decouple an object that invokes an operation from the provider object that implements.
The idea is to put a Command
object between the two, implementing an interface with a single method, execute
, which calls some method in the Receiver to perform the desired operation.