| name | Design Patterns |
| description | Reusable solutions to common software design problems using established architectural patterns |
| category | software-development |
Design Patterns
What I do
I provide proven, reusable solutions to recurring software design problems. Design patterns are not finished code but templates for how to solve a problem in a way that is reusable and maintainable. They represent best practices evolved over time by experienced developers facing similar challenges. I help you identify appropriate patterns for your specific context, implement them correctly, and combine them effectively to build robust software architectures.
When to use me
Use design patterns when you encounter common architectural problems that have known solutions. Patterns are most valuable when you have a team because they provide a shared vocabulary and standard approach to solving problems. Apply patterns when building complex systems that need to be extensible, when you need to communicate design intent clearly to other developers, or when you want to leverage battle-tested solutions rather than reinventing solutions. Avoid over-engineering simple applications with unnecessary patterns.
Core Concepts
- Creational Patterns: Factory Method, Abstract Factory, Builder, Singleton, Prototype
- Structural Patterns: Adapter, Bridge, Composite, Decorator, Facade, Flyweight, Proxy
- Behavioral Patterns: Chain of Responsibility, Command, Interpreter, Iterator, Mediator, Memento, Observer, State, Strategy, Template Method, Visitor
- Pattern Intent: The core problem each pattern solves
- Participants: The classes and objects involved in a pattern
- Collaborations: How participants work together
- Consequences: Trade-offs of using a pattern
- Implementation: Practical considerations for code
- Related Patterns: Connections between patterns
Code Examples
Factory Method Pattern
from abc import ABC, abstractmethod
from typing import Optional
class Document(ABC):
@abstractmethod
def create_page(self) -> str:
pass
class Resume(Document):
def create_page(self) -> str:
return "Resume page with personal info, experience, education"
class Report(Document):
def create_page(self) -> str:
return "Report page with title, author, sections"
class DocumentFactory:
_registry: dict[str, type[Document]] = {}
@classmethod
def register(cls, doc_type: str, doc_class: type[Document]) -> None:
cls._registry[doc_type] = doc_class
@classmethod
def create(cls, doc_type: str) -> Document:
if doc_type cls._registry:
ValueError()
cls._registry[doc_type]()
DocumentFactory.register(, Resume)
DocumentFactory.register(, Report)
resume = DocumentFactory.create()
(resume.create_page())
Strategy Pattern
from abc import ABC, abstractmethod
from typing import Protocol
class PaymentStrategy(Protocol):
def pay(self, amount: float) -> bool:
...
class CreditCardPayment:
def __init__(self, card_number: str, expiry: str, cvv: str):
self.card_number = card_number
self.expiry = expiry
self.cvv = cvv
def pay(self, amount: float) -> bool:
print(f"Processing credit card payment of ${amount}")
return True
class PayPalPayment:
def __init__(self, email: str):
self.email = email
def pay(self, amount: float) -> bool:
print(f"Processing PayPal payment of ${amount} for {self.email}")
return
:
():
.items: [[, ]] = []
._payment_strategy: [PaymentStrategy] =
() -> :
.items.append((name, price))
() -> :
._payment_strategy = strategy
() -> :
total = (price _, price .items)
._payment_strategy:
ValueError()
._payment_strategy.pay(total)
cart = ShoppingCart()
cart.add_item(, )
cart.set_payment_strategy(CreditCardPayment(, , ))
cart.checkout()
Observer Pattern
from abc import ABC, abstractmethod
from typing import Protocol
class StockObserver(Protocol):
def update(self, symbol: str, price: float) -> None:
...
class StockMarket:
def __init__(self):
self._observers: list[StockObserver] = []
self._stock_prices: dict[str, float] = {}
def register(self, observer: StockObserver) -> None:
self._observers.append(observer)
def unregister(self, observer: StockObserver) -> None:
self._observers.remove(observer)
def set_price(self, symbol: str, price: float) -> None:
self._stock_prices[symbol] = price
self._notify(symbol, price)
def _notify(self, symbol: str, price: float) -> None:
observer ._observers:
observer.update(symbol, price)
:
():
.name = name
() -> :
()
market = StockMarket()
market.register(Investor())
market.register(Investor())
market.set_price(, )
Decorator Pattern
from abc import ABC, abstractmethod
from typing import Protocol
class Coffee(Protocol):
def get_description(self) -> str:
pass
def get_cost(self) -> float:
pass
class Espresso:
def get_description(self) -> str:
return "Espresso"
def get_cost(self) -> float:
return 2.00
class CoffeeDecorator(Coffee):
def __init__(self, coffee: Coffee):
self._coffee = coffee
def get_description(self) -> str:
return self._coffee.get_description()
def get_cost(self) -> float:
return self._coffee.get_cost()
class ():
() -> :
._coffee.get_description() +
() -> :
._coffee.get_cost() +
():
() -> :
._coffee.get_description() +
() -> :
._coffee.get_cost() +
coffee = Sugar(Milk(Espresso()))
()
Singleton Pattern
class DatabaseConnection:
_instance: 'DatabaseConnection | None' = None
_connection: str | None = None
def __new__(cls):
if cls._instance is None:
cls._instance = super().__new__(cls)
return cls._instance
def connect(self, db_name: str) -> None:
if self._connection:
raise RuntimeError("Already connected")
self._connection = f"Connection to {db_name}"
print(f"Connected to {db_name}")
def query(self, sql: str) -> list[dict]:
if not self._connection:
raise RuntimeError("Not connected")
print(f"Executing: {sql}")
return [{"id": 1, "name": "Sample"}]
db1 = DatabaseConnection()
db1.connect("production_db")
db2 = DatabaseConnection()
(db1 db2)
db2.connect()
Best Practices
- Start Simple: Don't apply patterns prematurely; solve problems with simple code first
- Know the Trade-offs: Every pattern has benefits and costs; understand both before applying
- Prefer Composition: Favor object composition over class inheritance when possible
- Avoid God Objects: Don't let one class know too much or do too much
- Keep Cohesion High: Related functionality should be grouped together
- Minimize Coupling: Reduce dependencies between modules
- Open/Closed Principle: Software should be open for extension, closed for modification
- Use Patterns as Vocabulary: Share pattern names to communicate intent clearly
- Refactor to Patterns: Apply patterns during refactoring, not initial design
- Combine Patterns Wisely: Multiple patterns often work together; understand their interactions