Polymorphism

How can one variable hold many different types of objects? Master Polymorphism, the 'shapeshifting' power of OOP.

April 22, 20264 min read9 / 14

The word "Polymorphism" comes from Greek, meaning "Many Shapes." In Low-Level Design, it is one of the most powerful tools for writing clean, flexible code. It allows you to treat different objects as if they were the same thing.

The Essentials

The "Shapeshifting" guide:

  1. Many Shapes: One interface or parent reference can point to many different child implementations.
  2. Compile-time vs Runtime: You decide the "Reference" at compile-time, but the computer decides the "Behavior" at runtime.
  3. Flexibility: You can add new child types without changing the code that uses the parent.
  4. Generalization: Instead of writing code for GelPen and BallPen separately, you write code for a Pen.

The Remote Analogy (Revisited)

Think back to our Remote (Reference) and TV (Object) analogy.

  1. The Standard Remote: Imagine a "Universal Remote" labeled Animal.
  2. The Different TVs: You can point this remote at a Dog, a Cat, or a Lion.
  3. The Same Button: When you press the "Speak" button on the remote, the Dog will bark, the Cat will meow, and the Lion will roar.

The remote doesn't care what kind of animal it is. It only cares that the object it's pointing to is an "Animal" and knows how to "Speak."

Polymorphism in Action: The Shapeshifting Problem

The If/Else Trap

Without polymorphism, you must check the type of every object manually. Adding a new type requires "surgery" on your logic.

for (user of users) {
  if (user.type == "Student") { .. }
  else if (user.type == "Mentor") { .. }
}

This code is rigid and breaks whenever you add a new role to the system.

Polymorphic Flow

With polymorphism, the code only knows about the parent User. Each child handles its own unique behavior.

for (user of users) {
  user.login();
}

// New user? Just add a class.
// The loop stays the same!

The system is extensible. You can add 100 new user types without touching the main logic.

Code Implementation: Treating Many as One

Here is how you implement polymorphic behavior using a shared parent reference:

abstract class Animal { abstract speak(): void; } class Dog extends Animal { speak() { console.log("Barking!"); } } class Cat extends Animal { speak() { console.log("Meowing!"); } } // Treat a list of many types as one list of "Animals" const zoo: Animal[] = [new Dog(), new Cat(), new Dog()]; zoo.forEach(animal => { animal.speak(); // Each responds in its own shape! });

Why It Matters: Avoiding the "If/Else" Trap

Without polymorphism, your code would look like this:

  • "If user is Student, do X."
  • "If user is Instructor, do Y."
  • "If user is Mentor, do Z."

Every time you add a new user type, you have to find every if/else block in your entire app and update it. This is "Surgery" on your codebase.

With polymorphism, you just create a new class. The existing for loop doesn't change at all. It just works.

Now that we know the power of taking many forms, let's look at how we specialize behavior through Method Overriding.

Practice what you just read.

Polymorphism: Payment Gateway
1 exercise