Composition over inheritance
Skill morzecrew/agent-skills/skills/composition-over-inheritance
Collection of agent skills
npx -y skills add morzecrew/agent-skills --skill composition-over-inheritanceAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
Prefer composition and interfaces over class inheritance to reduce coupling and survive change - model has-a relationships, abstract through interfaces/contracts rather than parent classes, and reserve inheritance for the narrow cases where it fits. Use when designing or refactoring class hierarchies, reviewing OOP code, deciding between extending a class and composing one, or when the user mentions inheritance, subclassing, base classes, or composition.
SKILL.md
4.7 KB, as published. Nobody here has run it
Composition Over Inheritance
Inheritance looks like free code reuse, but it quietly couples a child to the full structure of its parent. That coupling is fine until requirements change — and then it becomes the most expensive kind of refactor. The default that ages better is composition (model has-a relationships) plus interfaces for abstraction, reaching for inheritance only in the narrow cases where it genuinely fits.
Use this skill when
- Designing a new class relationship and tempted to
extends/subclass. - Refactoring a hierarchy that has grown rigid or has deep parent chains.
- Reviewing OOP code where subclasses inherit methods that don't apply to them.
- The user mentions inheritance, subclassing, base classes, mixins, or composition.
Do not use this skill when
- A framework or library requires subclassing a specific base class to integrate.
- The language idiom for the case is inheritance (e.g. defining an interface/ABC to be implemented).
Why inheritance breaks down
When a child inherits from a parent, the parent's whole structure is thrust onto
the child — including methods that make no sense for it. Consider an Image with
resize/flip that also has load/save. A subclass that only needs resize
and flip is now forced to carry load/save too.
The fix forces structural surgery: you extract an intermediate parent (e.g.
FileImage) to hold load/save. But that breaks every existing consumer that
expected those methods on Image, so you must edit all of them — an expensive,
rippling refactor. This is inheritance's core weakness: it breaks down exactly
when you need to change the code.
Composition: model has-a
Instead of inheriting behavior, hold the pieces you need as members and delegate to them. You use only the types you want, so there are no irrelevant inherited methods and far less surface area between objects — which means less friction as changes arrive.
class Car:
def __init__(self, engine: Engine, wheels: Wheels):
self._engine = engine
self._wheels = wheels
def start(self):
self._engine.start() # delegate
You can swap engine for an ElectricEngine without touching Car's own logic.
Abstraction through interfaces
Inheritance actually bundles two separate capabilities: code reuse and
abstraction (a parent's methods form a contract; a consumer thinks it has a
Parent but is handed a subclass). You can get the abstraction without the coupling
by defining an interface for the contract and composing implementations behind
it. The consumer depends on the interface, not on a concrete parent.
This pairs naturally with passing implementations in from outside rather than
hard-coding them (see dependency-injection if present, or just constructor
parameters).
Composition's costs (be honest)
Composition isn't free:
- Boilerplate — you initialize and hold the internal types explicitly.
- Wrapper methods — to expose an inner object's behavior you often write small methods that just forward the call.
These are real but usually cheaper than the coupling inheritance imposes, because they stay local instead of rippling across consumers.
When inheritance is still fine
- You're inside an existing system with highly repetitive code where you need to change exactly one thing and a subclass is the smallest, most local edit.
- A framework mandates subclassing a base class as its extension point.
- You're defining a genuine, stable is-a contract with no foreseeable need to recombine behaviors.
The point is not "inheritance is forbidden" — it's that composition should be the default and inheritance the deliberate exception.
Quick checklist
- Does the subclass inherit methods that don't apply to it? Prefer composition.
- Would future requirements force an intermediate parent class? That ripple is the warning sign.
- Do you need the abstraction but not the reuse? Use an interface + composition.
- Is inheritance mandated by a framework or a stable is-a contract? Then it's justified.
Related skills
naming-things—Base/Abstractparent names often signal an inheritance smell.self-documenting-code— clear interfaces document a contract better than a parent class.