Documentation Index
Fetch the complete documentation index at: https://resources.devweekends.com/llms.txt
Use this file to discover all available pages before exploring further.
🦎 What is Polymorphism?
The word polymorphism comes from Greek:- Poly = Many
- Morph = Forms
- For a TV: It turns on the TV
- For a AC: It turns on the air conditioner
- For Lights: It turns on the lights
🔊 The Classic Shape Example
🎵 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 callsattack()!
📚 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 likePaymentMethod.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
| Benefit | Description |
|---|---|
| Flexibility | Add new types without changing existing code |
| Simplicity | One interface for many implementations |
| Maintainability | Changes in one class don’t affect others |
| Testability | Easy to swap implementations for testing |
🧪 Practice Exercise
Challenge: Build a Notification System
Challenge: Build a Notification System
Create a notification system that can send messages through different channels:
- EmailNotification: Sends via email
- SMSNotification: Sends via text message
- PushNotification: Sends to mobile app
- SlackNotification: Sends to Slack channel
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
An interviewer says: 'Explain how polymorphism supports the Open/Closed Principle with a concrete example from a system you would design.'
An interviewer says: 'Explain how polymorphism supports the Open/Closed Principle with a concrete example from a system you would design.'
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.
Python uses duck typing. How does this compare to Java's interface-based polymorphism, and what are the trade-offs for LLD design?
Python uses duck typing. How does this compare to Java's interface-based polymorphism, and what are the trade-offs for LLD design?
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.
In the Canvas example, total_area() calls e.get_area() for every element including Text. But Text's area calculation is approximate. How would you handle this in a production system?
In the Canvas example, total_area() calls e.get_area() for every element including Text. But Text's area calculation is approximate. How would you handle this in a production system?
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.
🏃 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!