DEV Community

Cristian Sifuentes
Cristian Sifuentes

Posted on

5 - Polymorphism — Designing for Substitution, Not Reuse

Polymorphism — Designing for Substitution, Not Reuse

Polymorphism — Designing for Substitution, Not Reuse

Why polymorphism is about trust, not sharing code


Polymorphism is not a tool for reuse.

It is a tool for trust.

This is Part 5 of the series:

  1. Classes in C#: From First Principles to Architectural Mastery
  2. Abstract Class vs Interface — A Systems View
  3. Composition vs Inheritance — The Physics of Change
  4. Encapsulation & Information Hiding — Designing for Ignorance
  5. Polymorphism — Designing for Substitution, Not Reuse

This article reframes polymorphism as a systems property,

not an inheritance trick.


The Most Misunderstood OOP Principle

Most developers learn polymorphism as:

“Different classes sharing a base class.”

That explanation is incomplete — and misleading.

Polymorphism is not about sharing code.

It is about preserving meaning under substitution.


What Polymorphism Really Guarantees

Polymorphism answers one question:

“Can I replace this with something else and still be safe?”

If the answer is yes, polymorphism exists.

If the answer is no, inheritance is lying to you.


Substitution Is the Point

Stream s = new MemoryStream();
Enter fullscreen mode Exit fullscreen mode

This line works because:

  • expectations hold
  • semantics are preserved
  • behavior remains valid

The system does not care which stream it is using —

only that the contract is honored.

That is polymorphism.


Reuse Is an Accident (Sometimes a Bad One)

Code reuse often motivates inheritance:

public class FileLogger : LoggerBase
Enter fullscreen mode Exit fullscreen mode

But reuse alone does not justify polymorphism.

If replacing FileLogger with DatabaseLogger changes behavior in surprising ways, the abstraction is broken — even if code compiles.


Polymorphism Is a Social Contract

Every polymorphic abstraction makes a promise:

“You don’t need to know what I am —

only what I guarantee.”

Breaking that promise erodes trust across the system.

This is why polymorphism scales systems — or destroys them.


The Liskov Boundary

Liskov Substitution Principle is not academic.

It is the boundary between safety and surprise.

Ask:

  • Are preconditions strengthened?
  • Are postconditions weakened?
  • Are invariants preserved?

If not, substitution is unsafe.


Interfaces vs Abstract Classes (Revisited)

Interfaces excel at polymorphism because:

  • they define capability
  • they avoid shared state
  • they minimize hidden coupling

Abstract classes introduce:

  • identity
  • workflow
  • temporal constraints

Use polymorphism where behavioral equivalence matters,

not where reuse is convenient.


Polymorphism Without Inheritance

Modern C# supports polymorphism via:

  • interfaces
  • delegates
  • composition
  • higher-order functions
Func<Order, decimal> pricingStrategy;
Enter fullscreen mode Exit fullscreen mode

No hierarchy.
No base class.
Still polymorphic.


The Cost of Broken Polymorphism

When polymorphism fails:

  • code becomes defensive
  • conditionals replace dispatch
  • abstractions leak
  • fear spreads
if (stream is FileStream) { ... }
Enter fullscreen mode Exit fullscreen mode

This is a smell.
The system no longer trusts its abstractions.


Polymorphism as System Stability

Polymorphism:

  • localizes change
  • reduces coordination
  • enables parallel evolution

It is not about elegance.
It is about resilience.


The Mental Model to Keep

Polymorphism is substitutability plus trust.

Reuse is optional.
Correctness is not.


Final Takeaway

Junior developers ask:

“How do I reuse this code?”

Senior engineers ask:

“Can this be replaced without breaking meaning?”

That question defines real polymorphism.


✍️ Cristian Sifuentes

.NET / C# • Architecture • Systems Thinking

If substitution fails, polymorphism never existed.

Top comments (0)