Key Takeaways: OOP Design: Patterns and Principles
One-Sentence Summary
Good OOP design organizes code so that each class has one clear purpose, classes interact through clean interfaces rather than internal details, and common problems are solved with proven patterns — but knowing when not to use OOP is just as important as knowing how.
SOLID Principles (The Two You Need Now)
Principle
Rule
Practical Test
Single Responsibility (SRP)
A class should have one reason to change
Can you describe the class in one sentence without "and"?
Open/Closed (OCP)
Open for extension, closed for modification
Can you add new behavior without editing existing code?
Coupling and Cohesion
Concept
Goal
Diagnostic
Coupling
Loose — classes interact through public interfaces
"If I change this class's internals, does anything else break?"
Cohesion
High — all methods relate to one responsibility
"Do all methods in this class use the same attributes?"
Three Essential Design Patterns
Pattern
Problem It Solves
Key Players
Python Example
Strategy
Swap algorithms at runtime
Strategy ABC, Concrete strategies, Context
Combat styles, pricing rules, formatters
Observer
Multiple objects react to state changes
Observer ABC, Concrete observers, Subject
Grade change notifications, event systems
Factory
Centralize object creation logic
Factory class, Product ABC, Concrete products
Report format selection, shape creation
Dataclasses: The Quick Reference
from dataclasses import dataclass, field
@dataclass # Auto-generates __init__, __repr__, __eq__
@dataclass(frozen=True) # Immutable instances
@dataclass(order=True) # Adds <, <=, >, >= comparisons
# Mutable defaults MUST use field():
tags: list[str] = field(default_factory=list)
# Exclude from repr or comparison:
internal_id: int = field(repr=False, compare=False)
Common Code Smells
Smell
Fix
God Class (does everything)
Split into focused classes (SRP)
Long Parameter List
Group parameters into a dataclass
Duplicated Code
Extract shared logic into a method/class
Feature Envy
Move the method to the class whose data it uses
Shotgun Surgery
Reduce coupling between classes
Primitive Obsession
Create a class/dataclass for the concept
When to Use OOP vs. Functions
Use OOP
Use Functions
Multiple instances with individual state
One-off scripts, no persistent state
Polymorphism needed
Data transformation pipelines
Domain entities with behavior
Simple input-output processing
Building extensible libraries
Stateless utilities
TaskFlow v1.5 Improvements
Task is now a @dataclass — minimal boilerplate
Observer pattern for notifications — TaskManager doesn't know about display details
Loose coupling — swap ConsoleNotifier for EmailNotifier without touching TaskManager
Key Insight
Start with functions. Refactor into classes when you notice repeated patterns of passing the same data between functions, or when you need multiple instances with their own state. Don't design a class hierarchy on day one — let the design emerge.
What's Next
Chapter 17: Introduction to algorithms and Big-O thinking — measuring not just whether code works, but how fast it works.