Design Patterns Tutorial 0/70 lessons ~6 min read Lesson 8

    OOP Recap for Pattern Thinkers

    Most OOP refreshers teach the four pillars as definitions.

    Course progress0%
    Focus
    21 guided sections
    Practice signal
    Examples included
    Career prep
    Interview Q&A included

    Introduction

    Most OOP refreshers teach the four pillars as definitions. This lesson teaches them as enablers of specific patterns. Encapsulation makes State and Memento possible. Polymorphism powers Strategy, Command, and Observer. Abstraction is what Adapter, Facade, and Repository sell. Inheritance — used sparingly — powers Template Method.

    Understanding which pillar a pattern leans on tells you when the pattern will feel natural in TypeScript, awkward in Go, or replaceable with a closure.

    The story

    A reviewer rejected a junior's PR: "this is just polymorphism, not Strategy." The junior pushed back: "Same code, different vocabulary?" The reviewer explained: "Polymorphism is the mechanism; Strategy is the decision to use it at the algorithm seam. Naming the pattern signals intent to the next reader." The junior renamed it Strategy in the ADR. PR merged.

    The business problem

    Teams that confuse OOP mechanisms with pattern intent pay in review friction and wrong abstractions:

    • Rejected refactors: correct polymorphism dismissed as over-engineering because intent wasn't named.
    • Language dismissal: "patterns don't work in functional JS" — when closures express the same intent.
    • Inheritance reflex: deep class trees where composition + interfaces would suffice.
    • Interview gaps: candidates describe syntax without mapping pillars to pattern choices.

    The problem teams faced

    Before connecting OOP to patterns, teams stumble on four predictable confusions:

    • Conflating "I used an interface" with "I applied a pattern."
    • Assuming patterns require classes — many are functions in modern codebases.
    • Using inheritance as the default reuse mechanism instead of composition.
    • Memorizing pattern UML without knowing which OOP pillar each pattern requires.

    Understanding the topic

    Four pillars → pattern families. Learn this map before the GoF catalog:

    • Encapsulation — State, Memento, Proxy, Singleton (hidden delegate / private state).
    • Polymorphism — Strategy, Command, Observer, Decorator, Iterator, Visitor.
    • Abstraction — Adapter, Facade, Bridge, Repository, Dependency Injection.
    • Inheritance — primarily Template Method; avoided elsewhere in modern code.

    Internal architecture

    Pillar → pattern reference:

    text
    Encapsulation → State, Memento, Proxy, Singleton
    Polymorphism → Strategy, Command, Visitor, Observer, Decorator
    Abstraction → Adapter, Facade, Bridge, Repository, DI
    Inheritance → Template Method (exception, not default)

    Visual explanation

    Three diagrams connect OOP pillars to pattern thinking. The first maps pillars to pattern families. The second shows mechanism vs intent. The third shows choosing the simplest expression in modern TypeScript.

    OOP pillars → pattern families
    Encapsulation
    State · Memento · Proxy
    Polymorphism
    Strategy · Observer · Command
    Abstraction
    Adapter · Facade · Repository
    Inheritance
    Template Method mainly
    Polymorphism powers the most patterns; inheritance powers the fewest.
    Mechanism vs pattern intent
    Polymorphism
    Language mechanism
    Change point
    Algorithm varies
    Strategy
    Named design decision
    ADR documents
    Intent for reviewers
    Same code can be 'just polymorphism' or Strategy — intent and ADR decide.
    Simplest valid expression
    Need varies?
    Evidence first
    Function type
    1-method Strategy
    Interface
    Multi-method contract
    Class hierarchy
    Template Method only
    Climb complexity only when the force demands it — not because OOP allows it.

    Informative example

    Same Strategy intent — three expressions:

    ts
    // Class-based (classic GoF)
    interface PricingStrategy { total(cart: Cart): Money; }
    class Checkout { constructor(private pricing: PricingStrategy) {} }
    // Function type (modern TS — same intent, less ceremony)
    type PricingFn = (cart: Cart) => Money;
    class CheckoutFn { constructor(private pricing: PricingFn) {} }
    // Decorator without classes (Express middleware = Decorator pattern)
    const withLogging = (handler: RequestHandler): RequestHandler =>
    (req, res, next) => { console.log(req.path); return handler(req, res, next); };

    All three use polymorphism. Only the first looks like a textbook diagram — all three deserve pattern names in ADRs when the force is real.

    Execution workflow

    1Apply OOP thinking to a pattern choice
    1 / 5

    Name the primary pillar

    Which OOP feature does this pattern fundamentally require?

    Strategy = polymorphism; Memento = encapsulation.

    Real-world use

    React Hooks are composition + polymorphism without class hierarchies. Express middleware is Decorator. Spring beans are DIP + polymorphism. Kotlin's migration from Java often replaces inheritance trees with interfaces + delegation — same patterns, less ceremony. The intent survives; the syntax adapts.

    Production case study

    A team migrated 50K lines from Java to Kotlin during a platform rewrite:

    • Symptom: 30% of class hierarchies had one or two subclasses — fragile base classes everywhere.
    • Decision: replace inheritance with interfaces + composition during migration.
    • Outcome: cyclomatic complexity down 25%; test setup time down 40%; same patterns expressed with less code.
    • Lesson: pillars are tools; pattern intent outlives the syntax.

    Trade-offs

    • Pro: pillar awareness tells you which patterns fit your language.
    • Pro: closures and modules often replace classes without losing intent.
    • Con: easy to conflate mechanism with pattern name.
    • Con: OOP courses teach inheritance first — teams reach for it reflexively.

    Decision framework

    • Use this lens before each pattern category in the course.
    • Use when porting code between languages with different OOP support.
    • Use in reviews to explain why a functional implementation still deserves a pattern name.
    • Avoid using OOP vocabulary to justify unnecessary classes in TypeScript.

    Best practices

    • Map each pattern you apply to its primary pillar in the ADR.
    • Prefer function types for single-method polymorphism in TypeScript.
    • Use closures for encapsulation when classes add no value.
    • Reserve `extends` for Template Method and documented framework bases.

    Anti-patterns to avoid

    • Deep inheritance trees to reuse one method — extract a component instead.
    • "It's not a pattern, it's just an interface" — if the force is real, name the pattern.
    • Assuming patterns require Java-style classes in React/Go/Rust codebases.

    Common mistakes

    • Teaching UML before pillars — students memorize shapes without forces.
    • Rejecting valid refactors because they don't match 1994 C++ samples.
    • Using encapsulation via global singletons instead of module scope or DI.

    Debugging tips

    • If a "pattern" has one implementation and no force, it's probably just polymorphism — verify git log.
    • When behavior differs by subtype in tests, check LSP — inheritance may be the wrong pillar.
    • Trace Decorator chains in middleware — log entry/exit to find which wrapper breaks.

    Optimization strategies

    • Batch-learn creational/structural/behavioral patterns by primary pillar — compare trade-offs side by side.
    • For interviews, state pillar + pattern + production story in one breath.

    Common misconceptions

    • Many believe patterns require classes. In reality, functions, modules, and hooks express the same boundaries.
    • Many believe Go/Rust can't do patterns. In reality, traits and interfaces cover most of the catalog; Template Method is the awkward one.
    • Related: SOLID section next — discipline that keeps OOP from going wrong.

    Advanced interview questions

    Interview Prep

    Practice concise answers, then expand each card for the explanation.

    4 questions
    1IntermediateQuestionWhich pillar does Strategy depend on?+

    Answer

    Polymorphism — one interface, many implementations. Encapsulation helps but the engine is substitutability.

    Follow-up

    Implement Strategy in JavaScript without TypeScript.
    2IntermediateQuestionCan you implement Decorator without classes?+

    Answer

    Yes — higher-order functions wrapping behavior. Express middleware is Decorator. `withLogging(fn)` applies the intent with zero classes.

    Follow-up

    Show a 5-line example.
    3AdvancedQuestionWhy is inheritance discouraged?+

    Answer

    Fragile base class, LSP traps, ripple effects when parent changes. Composition + interfaces deliver polymorphism with less coupling.

    Follow-up

    When is inheritance still right?
    4AdvancedQuestionWhat does encapsulation buy Memento?+

    Answer

    Snapshot state without exposing internals — caretaker holds opaque memento; originator controls restoration.

    Follow-up

    How without private fields?

    Summary

    You can map OOP pillars to pattern families, choose the simplest expression in your language, and explain mechanism vs intent in review. Tell the Strategy reviewer story — if you can do that, you're ready for SOLID.

    Ready to mark this lesson complete?Track your journey across the entire course.