Skip to main content

🦎 What is Polymorphism?

The word polymorphism comes from Greek:
  • Poly = Many
  • Morph = Forms
So polymorphism means “many forms”! Imagine a universal remote control 📺. You press the “ON” button and:
  • For a TV: It turns on the TV
  • For a AC: It turns on the air conditioner
  • For Lights: It turns on the lights
Same button, different results based on what you’re controlling!
Simple Definition: Polymorphism = Same method name, different behavior depending on the object
A deeper analogy: Think of a power outlet in your wall. The outlet provides one standard interface (two or three prongs). You can plug in a lamp, a phone charger, a blender, or a laptop — each device does something completely different with the same electricity. The outlet does not need to know what is plugged in, and you do not need a different outlet for each device. That is polymorphism: one interface, unlimited implementations.

🔊 The Classic Shape Example

Output:

🎵 Real-World Example: Music Player

Imagine a music player that can play different types of audio:

🎮 Fun Example: Game Attacks

Every character attacks differently, but the game just calls attack()!

📚 Types of Polymorphism

Method Overriding

Same method name in parent and childChild provides its own version

Duck Typing

If it walks like a duck…Python doesn’t check type, just method

🦆 Duck Typing Explained

“If it walks like a duck and quacks like a duck, it must be a duck!”
Python doesn’t care about the TYPE, only about whether the object has the method:

🎨 Practical Example: Drawing Application


💳 Example: Payment System

Different payment methods, same interface:

Why Polymorphism Is the Heart of Good Design

Polymorphism is not just a convenience — it is the mechanism that makes the Open/Closed Principle (which you will learn in the SOLID section) possible. When your code depends on an abstract interface like PaymentMethod.pay() rather than a concrete class like CreditCard.charge_visa(), you can add new payment methods (Apple Pay, crypto, buy-now-pay-later) without modifying the checkout code at all. A senior engineer would say: “If encapsulation protects your data and inheritance organizes your types, polymorphism is what makes your system genuinely extensible. Every well-designed plugin system, every driver architecture, every strategy pattern — they all depend on polymorphism.” Production example: Consider how Python’s sorted() function works. It does not know whether it is sorting integers, strings, or custom objects. It just calls each object’s comparison methods. That is polymorphism in the standard library — the sorting algorithm is written once but works with infinite types.

Benefits of Polymorphism


🧪 Practice Exercise

Create a notification system that can send messages through different channels:
  1. EmailNotification: Sends via email
  2. SMSNotification: Sends via text message
  3. PushNotification: Sends to mobile app
  4. SlackNotification: Sends to Slack channel
All should have a send(message) method!

Interview Insight

Key interview pattern: When designing any system in an LLD interview, polymorphism is your secret weapon for handling variability. The interviewer says “now what if we need to support a new notification channel?” If your design uses polymorphism (a NotificationChannel interface with send()), the answer is “just add a new class that implements the interface.” If your design uses if/elif chains, the answer is “modify existing code and hope nothing breaks.” The first answer demonstrates extensible design; the second demonstrates brittle design. Always structure your LLD answers so that new requirements are handled by adding new classes, not by modifying existing ones. That is polymorphism in action.

Interview Deep-Dive

Strong Answer:
  • Polymorphism is the mechanism that makes OCP work. When you define a NotificationChannel interface with send(message), the NotificationService iterates over channels and calls send() on each one. Adding a new channel (WhatsApp, Telegram, Discord) means creating a new class that implements the interface. The NotificationService never changes — it is closed for modification but open for extension.
  • Without polymorphism, adding a new channel means adding an elif branch to the service: “if channel == ‘whatsapp’: send_whatsapp()” — modifying existing code, risking regressions, and increasing cyclomatic complexity.
  • In the payment system from this page, the Checkout class calls payment_method.pay(amount) polymorphically. It works with CreditCard, PayPal, Crypto, and ApplePay without knowing or caring which one. When the business adds GooglePay next quarter, zero lines of Checkout code change.
  • This is why I always reach for an interface/ABC when I see behavior that varies across types. It is the single most impactful design decision in an LLD interview.
Follow-up: Is there a cost to polymorphism? When is a simple if/elif chain actually better?Polymorphism adds indirection: more classes, more files, and the actual implementation is resolved at runtime. For two or three types that rarely change, an if/elif chain is simpler, more readable, and faster to write. I reach for polymorphism when I expect the list of types to grow, when different teams own different implementations, or when I need to test each implementation in isolation. The crossover point is usually around three to four types.
Strong Answer:
  • In Java, polymorphism requires an explicit contract: the class must declare that it implements the interface. If MusicPlayer expects AudioFile, only classes that extend/implement AudioFile can be passed. The compiler enforces this at compile time.
  • In Python, duck typing means any object with a play() method can be passed to a function that calls play(), regardless of whether it formally implements an AudioFile interface. This is more flexible but less safe — you find type errors at runtime, not compile time.
  • For LLD design, I use ABCs (abstract base classes) to get the best of both worlds. By defining an AudioFile ABC with @abstractmethod play(), I get: (1) documentation of the contract, (2) an error at instantiation time if a subclass forgets to implement play(), and (3) compatibility with isinstance() checks and type hints.
  • The trade-off: ABCs add ceremony (import ABC, add decorators, define abstract methods) but provide safety and documentation. Pure duck typing is faster to write but harder for a new team member to understand — they must read the code to discover what methods are expected.
Follow-up: What is Python’s Protocol (from typing module) and when would you use it instead of ABC?Protocol provides structural subtyping — like duck typing but with static type checking. A class satisfies a Protocol if it has the required methods, without explicitly inheriting from it. Use Protocol when you want type checking for third-party classes you do not control (you cannot make them inherit from your ABC). Use ABC when you control the hierarchy and want runtime enforcement via isinstance().
Strong Answer:
  • This is a subtle LSP concern. The Shape contract implies area() returns the precise geometric area. Text’s implementation returns an approximation. If consumer code depends on precision (e.g., for physics simulation or cost calculation), Text violates the contract.
  • Solution 1: Separate the interfaces. Create a MeasurableArea interface for shapes with precise area calculations, and a separate Renderable interface for things that can be drawn. Text implements Renderable but not MeasurableArea. The total_area() function accepts only MeasurableArea objects, excluding Text.
  • Solution 2: Make the approximation explicit. Add a is_area_exact() method to the interface that returns True for geometric shapes and False for Text. The caller can decide whether to include approximate areas.
  • Solution 3: Use the Visitor pattern. A CanvasAreaCalculator visitor visits each element and decides how to handle it based on type — skipping Text or flagging it as approximate. This centralizes the decision logic.
  • The key insight: polymorphism works best when all implementations truly honor the same contract. When they do not, the right fix is to split the interface (ISP), not to paper over the difference.
Follow-up: This sounds like Interface Segregation. How do all four OOP pillars connect in a real design?They reinforce each other. Encapsulation protects each class’s internal state. Inheritance or composition defines the hierarchy. Polymorphism enables uniform treatment of different types. Abstraction defines the contracts that polymorphism relies on. When one pillar is weak (e.g., polymorphism with a leaky contract), the fix often comes from another pillar (abstraction via ISP). A senior engineer thinks about all four simultaneously, not in isolation.

🏃 Next Up: Abstraction

Now let’s learn how to hide complex details and show only what’s necessary!

Continue to Abstraction →

Learn how to hide complexity and create simple interfaces - like a car dashboard!