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:
- What is likely to change here? (requirements, implementations, platforms,
formats, policies…)
- 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:
- 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.
- 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.
- 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
- Restate the problem in terms of what changes and who depends on whom.
- Identify the natural responsibilities; propose one class per responsibility
(SRP).
- Find the seams where variation is likely and introduce abstractions there
(OCP, DIP) — interfaces or abstract base classes, not concrete dependencies.
- Only reach for a pattern when a problem from the lists above clearly appears.
Name the pattern you are using and why.
- 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
- 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.
- Then check for missing patterns: is there a creation mess, an interface
mismatch, a behavioral tangle?
- 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
- Give the definition in plain language, then a one-sentence "why it matters".
- Show a tiny "before" snippet that violates the principle/pattern and an
"after" snippet that follows it — keep each under ~15 lines.
- Call out the smell the user would notice in real code, so they can spot it
themselves next time.
- Mention one common over-engineering trap, so they don't apply it blindly.
When the user is generating code
- 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."
- Write code that depends on abstractions (interfaces/protocols/abstract
classes), not concretions, at the seams that will change.
- Keep classes small and single-purpose. Prefer composition over inheritance
unless there is a genuine "is-a" relationship that respects LSP.
- 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.
1---2name: object-oriented-design3description: 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++, 4---56# Object-Oriented Design Skill78This skill gives you a working mental model of object-oriented design: the six9core principles that tell you *what good OOD looks like*, and the classic GoF10patterns that give you *reusable shapes* for common problems. Use it to design,11review, refactor, teach, and generate OO code.1213## How to think about this skill1415OOD is not about memorizing patterns — it is about managing **change** and16**dependency**. Every principle and pattern below exists for one underlying17reason: to let a system evolve without forcing you to rewrite working code.18When you apply this skill, keep asking yourself two questions:19201. *What is likely to change here?* (requirements, implementations, platforms,21 formats, policies…)222. *Who depends on whom, and could that dependency be reversed or narrowed?*2324If you can answer those, the right principle or pattern usually becomes obvious.2526## When to read the reference files2728The body of this file gives you the decision framework and a one-line summary of29each principle and pattern. That is enough for most conversations. Read a30reference file when you need depth — definitions, anti-patterns, a compact code31sketch, and "when to use / when not to use" guidance.3233- `references/principles.md` — the six principles in full (SRP, OCP, LSP, LoD,34 ISP, DIP). Read it when the user is asking *whether* a design is good, or when35 you are about to refactor for maintainability.36- `references/creational-patterns.md` — Simple Factory, Factory Method, Abstract37 Factory, Singleton, Builder, Prototype. Read it when the conversation is about38 *object construction*.39- `references/structural-patterns.md` — Facade, Adapter, Bridge, Proxy,40 Decorator, Flyweight. Read it when the conversation is about *composing41 objects* or adapting interfaces.42- `references/behavioral-patterns.md` — Template Method, Strategy, Chain of43 Responsibility, State, Command, Observer, Mediator. Read it when the44 conversation is about *how objects communicate* or *how behavior varies*.4546Read only the file that matches the topic — do not load all four at once.4748## The six principles at a glance4950| Abbr | Principle | One-line test |51|------|-----------|---------------|52| SRP | Single Responsibility | Does this class have only one reason to change? |53| OCP | Open-Closed | Can I add new behavior by adding code, not editing old code? |54| LSP | Liskov Substitution | Can a subclass stand in for its parent without surprises? |55| LoD | Law of Demeter | Does this method talk only to its direct friends? |56| ISP | Interface Segregation | Are clients forced to depend on methods they don't use? |57| DIP | Dependency Inversion | Do high-level modules depend on abstractions, not concretions? |5859These are not laws you enforce with a ruler. They are *forces* you balance. A60god-class violates SRP badly; a 3-line utility class split into three classes to61"follow SRP" violates it by over-engineering. Use judgment, and explain the62trade-off to the user when you make a call.6364## The patterns at a glance6566### Creational — *who creates the objects?*6768- **Simple Factory**: one class decides which concrete class to instantiate69 based on a parameter. Use when the set of variants is small and stable.70- **Factory Method**: defer creation to subclasses; each subclass picks the71 concrete type. Use when creation logic should vary independently of the72 client.73- **Abstract Factory**: a family of factories that produce *related* objects74 (e.g. a whole UI theme). Use when you need products that must match each75 other.76- **Singleton**: one instance, global access. Use sparingly — it is often a77 disguised global. Prefer dependency injection when you can.78- **Builder**: construct a complex object step by step. Use when an object has79 many optional parts or a long construction sequence.80- **Prototype**: clone an existing object instead of building from scratch. Use81 when creation is expensive or you need pre-configured templates.8283### Structural — *how do objects compose?*8485- **Facade**: a single simplified front-end over a complex subsystem. Use to86 give clients a clean entry point.87- **Adapter**: make an existing class's interface match what a client expects.88 Use when you can't modify the adaptee.89- **Bridge**: split an abstraction from its implementation so they can vary90 independently. Use to avoid an explosion of subclasses across two dimensions.91- **Proxy**: a stand-in that controls access to the real object (lazy load,92 access control, logging, remote). Use when you need to intercept access.93- **Decorator**: wrap an object to add behavior without subclassing. Use to94 stack responsibilities dynamically.95- **Flyweight**: share fine-grained objects to save memory. Use when you have96 huge numbers of similar objects.9798### Behavioral — *how do objects talk and decide?*99100- **Template Method**: define an algorithm skeleton in a base class, let101 subclasses fill in steps. Use when the overall flow is fixed but steps vary.102- **Strategy**: encapsulate interchangeable algorithms behind a common103 interface. Use to replace conditional logic that picks behavior.104- **Chain of Responsibility**: pass a request along a chain until someone105 handles it. Use when more than one object *might* handle a request.106- **State**: an object changes behavior as its internal state changes. Use to107 eliminate giant `switch`/`if` on state.108- **Command**: encapsulate a request as an object. Use for undo, queuing,109 logging, or parameterizing actions.110- **Observer**: one subject notifies many dependents of changes. Use for111 one-to-many notification without tight coupling.112- **Mediator**: centralize how a set of colleagues interact. Use to remove113 direct colleague-to-colleague dependencies (e.g. UI components).114115## Choosing a pattern116117Don't start from "which pattern should I use?". Start from the *problem*:1181191. **Object creation is messy or coupled** → creational. One variant? Simple120 Factory. Variant per subclass? Factory Method. Family of related products?121 Abstract Factory. Many optional parts? Builder. Expensive to build / need122 templates? Prototype. Truly need one? Singleton — but question it first.1232. **Interfaces don't match, or composition is awkward** → structural. Need a124 clean entry point? Facade. Wrong interface? Adapter. Two independent125 dimensions of variation? Bridge. Need to control access? Proxy. Need to add126 behavior dynamically? Decorator. Massive numbers of similar objects?127 Flyweight.1283. **Behavior varies, or objects talk too much** → behavioral. Fixed flow,129 variable steps? Template Method. Pick-an-algorithm? Strategy. Multiple130 possible handlers? Chain of Responsibility. Behavior depends on state?131 State. Need undo/queue/logging? Command. One-to-many notification? Observer.132 Many peers talking directly? Mediator.133134If two patterns seem to fit, pick the simpler one. A pattern is justified only135when the flexibility it adds is *actually going to be used*. Otherwise it is136ceremony.137138## Workflow: how to apply this skill139140### When the user is *designing* something new1411421. Restate the problem in terms of what changes and who depends on whom.1432. Identify the natural responsibilities; propose one class per responsibility144 (SRP).1453. Find the seams where variation is likely and introduce abstractions there146 (OCP, DIP) — interfaces or abstract base classes, not concrete dependencies.1474. Only reach for a pattern when a problem from the lists above clearly appears.148 Name the pattern you are using and why.1495. Sketch the class relationships (a short UML-style description is fine) and150 walk the user through how a *new requirement* would be added. If adding it151 requires editing stable classes, rethink.152153### When the user is *reviewing or refactoring* existing code1541551. Read the code and look for principle violations first — these are usually the156 root cause:157 - A class with many unrelated methods/fields → SRP.158 - `switch`/`if` ladders over types that keep growing → OCP (replace with159 polymorphism) or Strategy/State.160 - Subclasses that override parent behavior in surprising ways, or break the161 parent contract → LSP.162 - A method reaching deep into another object's collaborators (`a.b.c().d()`)163 → LoD.164 - Fat interfaces clients only partially use → ISP.165 - High-level code importing concrete low-level classes → DIP.1662. Then check for missing patterns: is there a creation mess, an interface167 mismatch, a behavioral tangle?1683. Propose the smallest change that removes the smell. Explain *why* it helps —169 name the principle or pattern and the force it balances. Avoid170 big-bang rewrites unless the user asks for one.171172### When the user is *learning or teaching*1731741. Give the definition in plain language, then a one-sentence "why it matters".1752. Show a tiny "before" snippet that violates the principle/pattern and an176 "after" snippet that follows it — keep each under ~15 lines.1773. Call out the *smell* the user would notice in real code, so they can spot it178 themselves next time.1794. Mention one common over-engineering trap, so they don't apply it blindly.180181### When the user is *generating code*1821831. Before writing, decide which principles and (if any) patterns apply and tell184 the user briefly: "I'll use Strategy here because the discount rule varies185 per region and may grow."1862. Write code that depends on abstractions (interfaces/protocols/abstract187 classes), not concretions, at the seams that will change.1883. Keep classes small and single-purpose. Prefer composition over inheritance189 unless there is a genuine "is-a" relationship that respects LSP.1904. After writing, do a quick self-review against the six principles and mention191 any trade-off you made consciously (e.g. "I kept this as one class because192 splitting it would add indirection without real benefit").193194## Language notes195196The patterns are language-agnostic, but the *mechanism* differs:197198- **Java / Kotlin / C# / Swift**: first-class interfaces/protocols and abstract199 classes — most patterns map directly.200- **C++**: use pure virtual classes for interfaces; prefer `std::unique_ptr` /201 `std::shared_ptr` for ownership in patterns like Proxy or Decorator.202- **Python**: duck typing and first-class functions let you implement Strategy,203 Observer, Command very lightly — don't force a Java-style class hierarchy when204 a callable will do. Use `abc.ABC` only when you want to enforce a contract.205- **TypeScript**: use interfaces and `abstract class`; for Singleton prefer the206 module-level singleton (a module is already single-instance) over a class with207 a private constructor.208- **Objective-C**: use protocols for interfaces; the demos in the original209 project this skill was built from are Objective-C, so the patterns translate210 cleanly to Swift too.211- **Go**: no classes or inheritance — favor composition and interfaces. Strategy212 and Decorator map to interfaces and wrapping structs; Template Method is less213 natural (use a function field). Don't force GoF class hierarchies onto Go.214215When in doubt, prefer the *lightest* mechanism the language offers that still216preserves the principle (decouple what changes, depend on abstractions).217218## A note on over-engineering219220The single most common failure mode when applying OOD knowledge is using a221pattern where plain code would do. A 20-line script with three `if` branches222does not need Strategy. A class used in exactly one place does not need an223interface. The principles and patterns in this skill are *tools for managing224change and dependency* — when there is no change to manage and no dependency to225control, the right answer is often the simplest code that works. Say this out226loud to the user when you see them (or yourself) reaching for a pattern227unnecessarily. Good design is invisible; it should not advertise how many228patterns it uses.