ehavioursWhat Is Polymorphism?
Polymorphism literally means “many forms”.
In object‑oriented programming, it means that one interface or function call can work with objects of different types, and each type can respond in its own way.
Examples in plain English:
You call
draw()on aShape, and aCircledraws a circle while aRectangledraws a rectangle.You call
send()on aNotifier, and an email notifier sends an email while an SMS notifier sends a text.
Key idea:
The caller only knows about the common interface.
The actual object type determines what happens at run time.
In Low Level Design, polymorphism lets you design flexible code where new behaviours can be added without changing the existing calling code.
Two Main Flavors: Compile‑Time vs Run‑Time
You will see two broad forms of polymorphism:
Compile‑time polymorphism (overloading)
Same function name, different parameter lists, resolved by the compiler.
Example: multiple
printmethods with different parameters.Not the main focus in LLD discussions, but good to know.
Run‑time polymorphism (overriding)
Same function name in a base class and derived classes.
Which implementation runs depends on the actual object at runtime.
Implemented in C++ using virtual functions and base‑class references/pointers.
For LLD, when people say “polymorphism”, they almost always mean run‑time polymorphism via overriding.
Compile‑Time Polymorphism (Method Overloading) – Quick Look
In C++, you can define multiple functions with the same name but different parameter lists:
The compiler decides which log to call based on the arguments:
Same method name, different forms.
This is compile‑time polymorphism because the choice is made by the compiler, not at runtime.
Run‑Time Polymorphism (Method Overriding) – Core for LLD
Run‑time polymorphism relies on:
A base class with at least one
virtualmethod.Derived classes that override that method.
Using a base‑class pointer or reference to call the method.
General pattern:
Using polymorphism:
The same function process works with any class derived from Base.
Which implementation runs depends on the actual object passed in.
This is the heart of polymorphism in LLD.
Example: Notification Polymorphism
Consider an LLD scenario where the system can notify users in different ways.
Common Interface
Different Implementation:
Code Using Polymorphism
Here:
sendOrderUpdatedepends only on theNotifierabstraction.You can pass any notifier implementation.
The same call
notifier.send(...)behaves differently depending on the actual object type.
This is polymorphism used exactly the way LLD problems expect: a clean interface, multiple implementations.
Why Polymorphism Is So Useful in LLD
Polymorphism gives several advantages when designing systems:
One interface, many implementations
Your high‑level code depends on abstractions, not concrete classes.
You can plug in new behaviors without changing the caller.
Extensibility
To add a new payment method, notification channel, or storage type, you just add a new derived class.
Existing code that uses the base interface does not need modification.
Cleaner code
Instead of big
if/elsechains orswitchstatements scattered everywhere, you encapsulate behavior in derived classes.Code that chooses “which implementation to use” is localized.
Testability
You can pass mock or fake implementations in tests, using the same interface.
In interviews, showing polymorphic design (one abstraction with several implementations) is a strong signal of good design thinking.
LLD‑Style Example: Pricing Strategy
Imagine a ride‑hailing app where rides can be priced in different ways: normal pricing, surge pricing, or discount pricing.
Strategy Interface
Different Strategies
Using the Strategy Polymorphically
The Ride class depends only on the abstract PricingStrategy.
You can add new strategies (e.g., festival pricing, loyalty pricing) later, and Ride does not need to change.
This is a classic polymorphic design that shows up in many LLD problems.
Guidelines for Using Polymorphism
Polymorphism is powerful, but should be used intentionally.
Good situations to use polymorphism:
You have a clear abstraction (e.g.,
Notifier,PaymentMethod,Storage,PricingStrategy).You expect multiple implementations, either now or in the future.
You want high‑level code to stay the same while low‑level behavior changes.
Things to be careful about:
Do not create inheritance hierarchies “just because”.
Prefer a clear interface or abstract base with focused responsibilities.
Avoid making hierarchies too deep or complicated.
Consider composition first if the relationship is not “is‑a”.
In LLD interviews, a simple, well‑designed interface with a couple of concrete implementations is usually better than an overcomplicated hierarchy.
Summary
Polymorphism is the ability to treat different types through a common interface and let each type provide its own behavior.
In C++ and LLD, this is mainly achieved using inheritance, abstract classes, and virtual functions.
You saw:
What polymorphism means and how it differs from simple inheritance.
The difference between compile‑time (overloading) and run‑time (overriding) polymorphism.
How to implement run‑time polymorphism in C++ using virtual functions and base‑class references or pointers.
Practical LLD‑style examples with notifiers and pricing strategies.
When polymorphism is useful and what design guidelines to keep in mind.