OOP Recap for Pattern Thinkers
Most OOP refreshers teach the four pillars as definitions.
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:
Encapsulation → State, Memento, Proxy, SingletonPolymorphism → Strategy, Command, Visitor, Observer, DecoratorAbstraction → Adapter, Facade, Bridge, Repository, DIInheritance → 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.
Informative example
Same Strategy intent — three expressions:
// 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
Name the primary pillar
Which OOP feature does this pattern fundamentally require?
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.
1IntermediateQuestionWhich pillar does Strategy depend on?+
Answer
Follow-up
2IntermediateQuestionCan you implement Decorator without classes?+
Answer
Follow-up
3AdvancedQuestionWhy is inheritance discouraged?+
Answer
Follow-up
4AdvancedQuestionWhat does encapsulation buy Memento?+
Answer
Follow-up
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.