Design Patterns¶
Creational¶
Abstract factory
Builder
Dependency injection
Lazy initialization
Multiton
Object pool
Prototype
Resource acquisition is initialization (RAII)
Factory method¶
Factory Method is a creational design pattern used to create concrete implementations of a common interface.
Implement multiple classes with a common interface but different behaviors
Object creation in a single factory for easy extension
# https://realpython.com/factory-method-python/
class SpotifyService:
def __init__(self, access_code):
self._access_code = access_code
def test_connection(self):
print(f'Accessing Spotify with {self._access_code}')
class SpotifyServiceBuilder:
def __init__(self):
self._instance = None
def __call__(self, spotify_client_key, spotify_client_secret, **_ignored):
if not self._instance:
access_code = self.authorize(
spotify_client_key, spotify_client_secret)
self._instance = SpotifyService(access_code)
return self._instance
def authorize(self, key, secret):
return 'SPOTIFY_ACCESS_CODE'
class PandoraService:
pass
class PandoraServiceBuilder:
pass
class ObjectFactory:
def __init__(self):
self._builders = {}
def register_builder(self, key, builder):
self._builders[key] = builder
def create(self, key, **kwargs):
builder = self._builders.get(key)
if not builder:
raise ValueError(key)
return builder(**kwargs)
factory = object_factory.ObjectFactory()
factory.register_builder('SPOTIFY', SpotifyServiceBuilder())
spotify = music.factory.create('SPOTIFY', **config)
spotify.test_connection()
Tutorials:
Singleton¶
Python modules are Singletons because there is only one instance of each module, and changes made to the module object get reflected everywhere.
# https://docs.python.org/3/faq/programming.html#how-do-i-share-global-variables-across-modules
# config.py:
x = 0 # Default value of the 'x' configuration setting
# mod.py:
import config
config.x = 1
# main.py
import config
import mod
print(config.x)
Structural¶
Adapter, Wrapper, or Translator
Bridge
Composite
Decorator
Extension object
Facade
Flyweight
Front controller
Marker
Module
Proxy
Twin
Behavioral¶
Blackboard
Chain of responsibility
Intepreter
Interator
Mediator
Memento
Null object
Servant
Specification
State
Template method
Visitor
Command¶
The command pattern is used to encapsulate all information needed to perform an action or trigger an event at a later time.
# https://github.com/ArjanCodes/2021-command-transactions
# with_transaction
@dataclass
class BankController:
ledger: list[Transaction] = field(default_factory=list)
current: int = 0
def register(self, transaction: Transaction) -> None:
del self.ledger[self.current :]
self.ledger.append(transaction)
self.current += 1
def undo(self) -> None:
if self.current > 0:
self.current -= 1
def redo(self) -> None:
if self.current < len(self.ledger):
self.current += 1
def compute_balances(self) -> None:
for transaction in self.ledger[: self.current]:
transaction.execute()
@dataclass
class Deposit:
account: Account
amount: int
@property
def transfer_details(self) -> str:
return f"${self.amount/100:.2f} to account {self.account.name}"
def execute(self) -> None:
self.account.deposit(self.amount)
print(f"Deposited {self.transfer_details}")
bank = Bank()
controller = BankController()
account1 = bank.create_account("ArjanCodes")
controller.register(Deposit(account1, 100000))
controller.undo()
controller.redo()
Tutorials:
Observer¶
aka Publisher/Subscriber
Allows multiple subscribers to register receive events from a single publisher.
Provides for loose coupling between publishers and subscribers.
# https://github.com/arjancodes/betterpython
# 4 - Observer Pattern - api v2
subscribers = dict()
def subscribe(event_type: str, fn):
if not event_type in subscribers:
subscribers[event_type] = []
subscribers[event_type].append(fn)
def post_event(event_type: str, data):
if not event_type in subscribers:
return
for fn in subscribers[event_type]:
fn(data)
def handle_user_registered_event(user):
post_slack_message("sales",
f"{user.name} has registered with email address {user.email}.
Please spam this person incessantly.")
def setup_slack_event_handlers():
subscribe("user_registered", handle_user_registered_event)
subscribe("user_upgrade_plan", handle_user_upgrade_plan_event)
def register_new_user(name: str, password: str, email: str):
user = create_user(name, password, email)
post_event("user_registered", user)
setup_slack_event_handlers()
register_new_user("Arjan", "BestPasswordEva", "hi@arjanegges.com")
Docs:
Tutorials:
Strategy¶
A behavioral software design pattern that enables selecting an algorithm at runtime. Instead of implementing a single algorithm directly, code receives run-time instructions as to which in a family of algorithms to use.
You can use functions in Python in place of classes if the strategy doesn’t store state.
# https://www.geeksforgeeks.org/strategy-method-python-design-patterns/
class Item:
"""Constructor function with price and discount"""
def __init__(self, price, discount_strategy = None):
"""take price and discount strategy"""
self.price = price
self.discount_strategy = discount_strategy
"""A separate function for price after discount"""
def price_after_discount(self):
if self.discount_strategy:
discount = self.discount_strategy(self)
else:
discount = 0
return self.price - discount
def __repr__(self):
statement = "Price: {}, price after discount: {}"
return statement.format(self.price, self.price_after_discount())
"""function dedicated to On Sale Discount"""
def on_sale_discount(order):
return order.price * 0.25 + 20
"""function dedicated to 20 % discount"""
def twenty_percent_discount(order):
return order.price * 0.20
"""main function"""
if __name__ == "__main__":
print(Item(20000))
"""with discount strategy as 20 % discount"""
print(Item(20000, discount_strategy = twenty_percent_discount))
"""with discount strategy as On Sale Discount"""
print(Item(20000, discount_strategy = on_sale_discount))
Tutorials:
Concurrency¶
Active object
Balking
Binding properties
Compute kernel
Double-checked locking
Event-based asynchronous
Guarded suspension
Join
Lock
Messaging design pattern (MDP)
Monitor object
Reactor
Read-write block
Scheduler
Thread pool
Thread-specific storage
Safe Concurrency with Exclusive Ownership
CPU atomic operation