Solution Architecture Practice

Comprehensive guide to software architecture frameworks, patterns, and best practices

View project on GitHub

Design Patterns Catalog

Overview

Classical object-oriented design patterns for solving recurring design problems. These patterns provide proven solutions for code organization, object creation, and behavioral interactions.


Creational Patterns

1. Factory Method

Problem: Creating objects with complex initialization logic.

Solution: Create via factory method instead of constructor.

# Without factory (hard to extend)
if type == "pdf":
    report = PDFReport()
elif type == "excel":
    report = ExcelReport()

# With factory (easy to extend)
class ReportFactory:
    @staticmethod
    def create(type):
        reports = {
            "pdf": PDFReport,
            "excel": ExcelReport,
            "html": HTMLReport
        }
        return reports[type]()

# Usage
report = ReportFactory.create("pdf")

2. Builder Pattern

Problem: Creating objects with many optional parameters.

Solution: Build object step-by-step with fluent API.

# Without builder (confusing)
order = Order(123, "John", "john@example.com", 
              "123 Main", "New York", "NY", "10001",
              "VISA", "4111...", "12/25")

# With builder (clear)
order = (OrderBuilder()
    .with_customer_id(123)
    .with_customer_name("John")
    .with_email("john@example.com")
    .with_address("123 Main", "New York", "NY", "10001")
    .with_payment("VISA", "4111...", "12/25")
    .build())

3. Singleton

Problem: Need single shared instance throughout application.

Solution: Ensure only one instance can exist.

class DatabaseConnection:
    _instance = None
    
    def __new__(cls):
        if cls._instance is None:
            cls._instance = super().__new__(cls)
            cls._instance.connect()
        return cls._instance

# Usage
db1 = DatabaseConnection()
db2 = DatabaseConnection()
assert db1 is db2  # Same instance

Structural Patterns

4. Adapter Pattern

Problem: Interface incompatibility between components.

Solution: Adapter translates between interfaces.

Old Interface → Adapter → New Interface
  legacy()                 modern()
class LegacySystem:
    def legacy_method(self):
        return "legacy data"

class ModernAdapter:
    def __init__(self, legacy):
        self.legacy = legacy
    
    def modern_method(self):
        return self.legacy.legacy_method()

# Usage
legacy = LegacySystem()
adapter = ModernAdapter(legacy)
data = adapter.modern_method()

5. Decorator Pattern

Problem: Add responsibilities to object dynamically.

Solution: Wrap object with decorator.

# Simple Logger
class Logger:
    def __init__(self, service):
        self.service = service
    
    def perform(self):
        print("Starting operation")
        result = self.service.perform()
        print("Operation complete")
        return result

# Usage
service = UserService()
logged = Logger(service)
cached = Cache(logged)
result = cached.perform()  # Logs + caches

6. Facade Pattern

Problem: Complex subsystem is hard to use.

Solution: Provide simplified interface (facade).

# Without facade (complex)
db = Database()
cache = Cache()
logger = Logger()
validator = Validator()

# 10 lines of complex interaction code

# With facade (simple)
service = UserService()
user = service.get_user(123)

7. Proxy Pattern

Problem: Need to control access to object.

Solution: Use proxy that controls access.

class UserServiceProxy:
    def __init__(self, service):
        self.service = service
    
    def get_user(self, user_id):
        # Check cache first
        if user_id in self.cache:
            return self.cache[user_id]
        
        # Load from service
        user = self.service.get_user(user_id)
        
        # Cache result
        self.cache[user_id] = user
        return user

Behavioral Patterns

8. Observer Pattern

Problem: Object changes need to notify many subscribers.

Solution: Observers register to be notified of changes.

class Subject:
    def __init__(self):
        self.observers = []
    
    def attach(self, observer):
        self.observers.append(observer)
    
    def notify(self, event):
        for observer in self.observers:
            observer.update(event)

# Usage
order = Order()
order.attach(EmailNotifier())
order.attach(InventoryUpdater())
order.complete()  # Both notified

9. Strategy Pattern

Problem: Multiple algorithms for same problem.

Solution: Strategy classes encapsulate algorithms.

class PaymentProcessor:
    def __init__(self, strategy):
        self.strategy = strategy
    
    def process(self, amount):
        return self.strategy.pay(amount)

class CreditCardPayment:
    def pay(self, amount):
        # Credit card logic
        pass

class PayPalPayment:
    def pay(self, amount):
        # PayPal logic
        pass

# Usage
processor = PaymentProcessor(CreditCardPayment())
processor.process(100)

processor = PaymentProcessor(PayPalPayment())
processor.process(100)

10. Command Pattern

Problem: Encapsulate requests as objects.

Solution: Command object encapsulates request.

class Command:
    def execute(self):
        pass

class CreateUserCommand(Command):
    def __init__(self, user_data):
        self.user_data = user_data
    
    def execute(self):
        return UserService.create(self.user_data)

# Usage
cmd = CreateUserCommand({"name": "John"})
cmd.execute()

Architectural Patterns

11. Model-View-Controller (MVC)

Problem: Need to separate concerns in UI application.

Solution: Separate Model (data), View (UI), Controller (logic).

User Input
    ↓
Controller (handle action)
    ↓
Model (update data)
    ↓
View (render UI)
    ↓
Display to user

12. Model-View-ViewModel (MVVM)

Problem: Data binding between UI and business logic.

Solution: ViewModel bridges Model and View.

View (UI) ←→ ViewModel ←→ Model (Data)
           (two-way binding)

References

Behavioral Patterns

  • Chain of Responsibility
  • Command
  • Iterator
  • Mediator
  • Memento
  • Observer
  • State
  • Strategy
  • Template Method
  • Visitor

Architectural Patterns

  • Layered Architecture
  • Event-Driven Architecture
  • Microkernel
  • Space-Based Architecture

Status: Placeholder
Last Updated: November 2025