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.