01 — SOLID Principles, Cohesion & Coupling

At a glance

After completing this lesson you can:

Time: ~50 minutes · Prerequisites: 03 — Graphs & Topological Scheduling

Reading Companion

Why this matters in production

"God classes" (such as a 3,000-line UserManager that hashes passwords, sends emails, queries Postgres, formats CSVs, and charges Stripe) are impossible to test without dozens of mocks. Changing email templates accidentally breaks payment processing. Applying Single Responsibility and Dependency Inversion creates modular, testable components where changes in one domain never cascade into outages in another.

Deep dive

1. High Cohesion vs Low Coupling

2. Pragmatic SOLID for Python Backends

Hands on

python3 phase1-cs-and-architecture/03-software-architecture/01-solid-and-cohesion/examples/01_demo.py
python3 phase1-cs-and-architecture/03-software-architecture/01-solid-and-cohesion/examples/02_trap.py

AI Trap & Code Review

# BAD AI-GENERATED CODE — THE OMNIPOTENT GOD CLASS
class OrderService:
    def process_order(self, order_data):
        # 1. Validation
        if not order_data.get("items"): raise ValueError()
        # 2. Database SQL
        conn = psycopg2.connect("...")
        conn.cursor().execute("INSERT INTO orders ...")
        # 3. Third-party HTTP call
        requests.post("https://api.stripe.com/charges", ...)
        # 4. SMTP email sending
        smtplib.SMTP("...").sendmail(...)

Exercises

  1. Rung 1 (calculate_cohesion_ratio): Measure module method-to-attribute sharing ratio (LCOM proxy) given a class interface description.
  2. Rung 2 (refactor_god_class — Fix the AI Bug): Decompose the monolithic OrderProcessor into separate OrderValidator, PaymentGateway, and NotificationService components.
  3. Rung 3 (PaymentStrategy Pattern): Implement Open/Closed payment handlers (CreditCardPayment, CryptoPayment) conforming to a unified PaymentStrategy Protocol.
  4. Rung 4 (InterfaceSegregation): Segregate a bloated StorageInterface into narrow ReadableStorage and WritableStorage protocols.
  5. Rung 5 (DependencyInversionContainer): Build a lightweight service container that wires abstractions to concrete implementations without global imports.

Wrapping up

Can you explain?

  1. What is the difference between high cohesion and low coupling?
  2. Why does the Dependency Inversion Principle make testing with in-memory Fakes straightforward?
  3. What is the single biggest architectural smell in a pull request?

Further Reading & References

Show reference solution notes

Solution & Engineering Notes — 01 SOLID & Architecture

  1. Pragmatic SRP: Single Responsibility does not mean one method per class. It means all methods in the class serve the same cohesive business actor or domain concept.
  2. Dependency Inversion in Python: By expressing interfaces with typing.Protocol, callers can inject mocks or fakes in unit tests without forcing production classes to inherit from ABCs.

Self-check

Run python3 -m unittest phase1-cs-and-architecture/03-software-architecture/01-solid-and-cohesion/test_exercises.py -v until green, then record it here. Saved to progress/rungs.json; commit it.

Confidence:

Completion edits PROGRESS.md; commit it with the lesson.