# Object Oriented Design

> Object-oriented design (OOD) guidance based on the six SOLID-family principles and the 23 classic GoF design patterns. Use this skill whenever the user is designing classes, modules, or object graphs; reviewing or refactoring existing object-oriented code; asking "which design pattern should I use", "is this design good", "how do I make this code more maintainable/extensible"; learning or teaching OOP concepts (SRP, OCP, LSP, ISP, DIP, Law of Demeter, factory, singleton, adapter, bridge, proxy, decorator, facade, flyweight, template method, strategy, state, command, observer, mediator, chain of responsibility, builder, prototype, abstract factory); or generating new code that should follow good OO practice. Trigger it even when the user does not explicitly say "design pattern" or "SOLID" — if they describe a problem about class responsibilities, coupling, inheritance hierarchies, object creation, state transitions, or inter-object communication, this skill applies. It works across any OO language (Java, C++,

- Skill: `tzf1003/object-oriented-design` (Agent Skill)
- Install (CLI): `npx skillmds@latest add tzf1003/object-oriented-design`
- Raw SKILL.md: https://api.skillmd.com/api/skills/tzf1003/object-oriented-design/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: tzf1003 (https://skillmd.com/u/tzf1003)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/tzf1003/object-oriented-design

---


# Object-Oriented Design Skill

This skill gives you a working mental model of object-oriented design: the six
core principles that tell you *what good OOD looks like*, and the classic GoF
patterns that give you *reusable shapes* for common problems. Use it to design,
review, refactor, teach, and generate OO code.

## How to think about this skill

OOD is not about memorizing patterns — it is about managing **change** and
**dependency**. Every principle and pattern below exists for one underlying
reason: to let a system evolve without forcing you to rewrite working code.
When you apply this skill, keep asking yourself two questions:

1. *What is likely to change here?* (requirements, implementations, platforms,
   formats, policies…)
2. *Who depends on whom, and could that dependency be reversed or narrowed?*

If you can answer those, the right principle or pattern usually becomes obvious.

## When to read the reference files

The body of this file gives you the decision framework and a one-line summary of
each principle and pattern. That is enough for most conversations. Read a
reference file when you need depth — definitions, anti-patterns, a compact code
sketch, and "when to use / when not to use" guidance.

- `references/principles.md` — the six principles in full (SRP, OCP, LSP, LoD,
  ISP, DIP). Read it when the user is asking *whether* a design is good, or when
  you are about to refactor for maintainability.
- `references/creational-patterns.md` — Simple Factory, Factory Method, Abstract
  Factory, Singleton, Builder, Prototype. Read it when the conversation is about
  *object construction*.
- `references/structural-patterns.md` — Facade, Adapter, Bridge, Proxy,
  Decorator, Flyweight. Read it when the conversation is about *composing
  objects* or adapting interfaces.
- `references/behavioral-patterns.md` — Template Method, Strategy, Chain of
  Responsibility, State, Command, Observer, Mediator. Read it when the
  conversation is about *how objects communicate* or *how behavior varies*.

Read only the file that matches the topic — do not load all four at once.

## The six principles at a glance

| Abbr | Principle | One-line test |
|------|-----------|---------------|
| SRP | Single Responsibility | Does this class have only one reason to change? |
| OCP | Open-Closed | Can I add new behavior by adding code, not editing old code? |
| LSP | Liskov Substitution | Can a subclass stand in for its parent without surprises? |
| LoD | Law of Demeter | Does this method talk only to its direct friends? |
| ISP | Interface Segregation | Are clients forced to depend on methods they don't use? |
| DIP | Dependency Inversion | Do high-level modules depend on abstractions, not concretions? |

These are not laws you enforce with a ruler. They are *forces* you balance. A
god-class violates SRP badly; a 3-line utility class split into three classes to
"follow SRP" violates it by over-engineering. Use judgment, and explain the
trade-off to the user when you make a call.

## The patterns at a glance

### Creational — *who creates the objects?*

- **Simple Factory**: one class decides which concrete class to instantiate
  based on a parameter. Use when the set of variants is small and stable.
- **Factory Method**: defer creation to subclasses; each subclass picks the
  concrete type. Use when creation logic should vary independently of the
  client.
- **Abstract Factory**: a family of factories that produce *related* objects
  (e.g. a whole UI theme). Use when you need products that must match each
  other.
- **Singleton**: one instance, global access. Use sparingly — it is often a
  disguised global. Prefer dependency injection when you can.
- **Builder**: construct a complex object step by step. Use when an object has
  many optional parts or a long construction sequence.
- **Prototype**: clone an existing object instead of building from scratch. Use
  when creation is expensive or you need pre-configured templates.

### Structural — *how do objects compose?*

- **Facade**: a single simplified front-end over a complex subsystem. Use to
  give clients a clean entry point.
- **Adapter**: make an existing class's interface match what a client expects.
  Use when you can't modify the adaptee.
- **Bridge**: split an abstraction from its implementation so they can vary
  independently. Use to avoid an explosion of subclasses across two dimensions.
- **Proxy**: a stand-in that controls access to the real object (lazy load,
  access control, logging, remote). Use when you need to intercept access.
- **Decorator**: wrap an object to add behavior without subclassing. Use to
  stack responsibilities dynamically.
- **Flyweight**: share fine-grained objects to save memory. Use when you have
  huge numbers of similar objects.

### Behavioral — *how do objects talk and decide?*

- **Template Method**: define an algorithm skeleton in a base class, let
  subclasses fill in steps. Use when the overall flow is fixed but steps vary.
- **Strategy**: encapsulate interchangeable algorithms behind a common
  interface. Use to replace conditional logic that picks behavior.
- **Chain of Responsibility**: pass a request along a chain until someone
  handles it. Use when more than one object *might* handle a request.
- **State**: an object changes behavior as its internal state changes. Use to
  eliminate giant `switch`/`if` on state.
- **Command**: encapsulate a request as an object. Use for undo, queuing,
  logging, or parameterizing actions.
- **Observer**: one subject notifies many dependents of changes. Use for
  one-to-many notification without tight coupling.
- **Mediator**: centralize how a set of colleagues interact. Use to remove
  direct colleague-to-colleague dependencies (e.g. UI components).

## Choosing a pattern

Don't start from "which pattern should I use?". Start from the *problem*:

1. **Object creation is messy or coupled** → creational. One variant? Simple
   Factory. Variant per subclass? Factory Method. Family of related products?
   Abstract Factory. Many optional parts? Builder. Expensive to build / need
   templates? Prototype. Truly need one? Singleton — but question it first.
2. **Interfaces don't match, or composition is awkward** → structural. Need a
   clean entry point? Facade. Wrong interface? Adapter. Two independent
   dimensions of variation? Bridge. Need to control access? Proxy. Need to add
   behavior dynamically? Decorator. Massive numbers of similar objects?
   Flyweight.
3. **Behavior varies, or objects talk too much** → behavioral. Fixed flow,
   variable steps? Template Method. Pick-an-algorithm? Strategy. Multiple
   possible handlers? Chain of Responsibility. Behavior depends on state?
   State. Need undo/queue/logging? Command. One-to-many notification? Observer.
   Many peers talking directly? Mediator.

If two patterns seem to fit, pick the simpler one. A pattern is justified only
when the flexibility it adds is *actually going to be used*. Otherwise it is
ceremony.

## Workflow: how to apply this skill

### When the user is *designing* something new

1. Restate the problem in terms of what changes and who depends on whom.
2. Identify the natural responsibilities; propose one class per responsibility
   (SRP).
3. Find the seams where variation is likely and introduce abstractions there
   (OCP, DIP) — interfaces or abstract base classes, not concrete dependencies.
4. Only reach for a pattern when a problem from the lists above clearly appears.
   Name the pattern you are using and why.
5. Sketch the class relationships (a short UML-style description is fine) and
   walk the user through how a *new requirement* would be added. If adding it
   requires editing stable classes, rethink.

### When the user is *reviewing or refactoring* existing code

1. Read the code and look for principle violations first — these are usually the
   root cause:
   - A class with many unrelated methods/fields → SRP.
   - `switch`/`if` ladders over types that keep growing → OCP (replace with
     polymorphism) or Strategy/State.
   - Subclasses that override parent behavior in surprising ways, or break the
     parent contract → LSP.
   - A method reaching deep into another object's collaborators (`a.b.c().d()`)
     → LoD.
   - Fat interfaces clients only partially use → ISP.
   - High-level code importing concrete low-level classes → DIP.
2. Then check for missing patterns: is there a creation mess, an interface
   mismatch, a behavioral tangle?
3. Propose the smallest change that removes the smell. Explain *why* it helps —
   name the principle or pattern and the force it balances. Avoid
   big-bang rewrites unless the user asks for one.

### When the user is *learning or teaching*

1. Give the definition in plain language, then a one-sentence "why it matters".
2. Show a tiny "before" snippet that violates the principle/pattern and an
   "after" snippet that follows it — keep each under ~15 lines.
3. Call out the *smell* the user would notice in real code, so they can spot it
   themselves next time.
4. Mention one common over-engineering trap, so they don't apply it blindly.

### When the user is *generating code*

1. Before writing, decide which principles and (if any) patterns apply and tell
   the user briefly: "I'll use Strategy here because the discount rule varies
   per region and may grow."
2. Write code that depends on abstractions (interfaces/protocols/abstract
   classes), not concretions, at the seams that will change.
3. Keep classes small and single-purpose. Prefer composition over inheritance
   unless there is a genuine "is-a" relationship that respects LSP.
4. After writing, do a quick self-review against the six principles and mention
   any trade-off you made consciously (e.g. "I kept this as one class because
   splitting it would add indirection without real benefit").

## Language notes

The patterns are language-agnostic, but the *mechanism* differs:

- **Java / Kotlin / C# / Swift**: first-class interfaces/protocols and abstract
  classes — most patterns map directly.
- **C++**: use pure virtual classes for interfaces; prefer `std::unique_ptr` /
  `std::shared_ptr` for ownership in patterns like Proxy or Decorator.
- **Python**: duck typing and first-class functions let you implement Strategy,
  Observer, Command very lightly — don't force a Java-style class hierarchy when
  a callable will do. Use `abc.ABC` only when you want to enforce a contract.
- **TypeScript**: use interfaces and `abstract class`; for Singleton prefer the
  module-level singleton (a module is already single-instance) over a class with
  a private constructor.
- **Objective-C**: use protocols for interfaces; the demos in the original
  project this skill was built from are Objective-C, so the patterns translate
  cleanly to Swift too.
- **Go**: no classes or inheritance — favor composition and interfaces. Strategy
  and Decorator map to interfaces and wrapping structs; Template Method is less
  natural (use a function field). Don't force GoF class hierarchies onto Go.

When in doubt, prefer the *lightest* mechanism the language offers that still
preserves the principle (decouple what changes, depend on abstractions).

## A note on over-engineering

The single most common failure mode when applying OOD knowledge is using a
pattern where plain code would do. A 20-line script with three `if` branches
does not need Strategy. A class used in exactly one place does not need an
interface. The principles and patterns in this skill are *tools for managing
change and dependency* — when there is no change to manage and no dependency to
control, the right answer is often the simplest code that works. Say this out
loud to the user when you see them (or yourself) reaching for a pattern
unnecessarily. Good design is invisible; it should not advertise how many
patterns it uses.

