Learn polymorphism by applying it in a practical scenario.
Learn polymorphism by applying it in a practical scenario.
Taking up the OOP Essentials: Inheritance Byte is a prerequisite as the current Byte builds on top of the codebase used there.
Polymorphism is about being able to change the functionality offered by a method, depending on the situation.
This is achieved in two ways
Method Overriding - Child class implements a method that is already present in the parent class, resulting in the child method being invoked - Dynamic polymorphism.
Method Overloading - Writing multiple methods with the same name but with different sets of parameters (or order of parameters) so that the appropriate method gets invoked based on the parameters passed - Static polymorphism. This may not be supported cleanly in some languages (e.g. Python).
In this Byte, we will learn method overriding and touch upon method overloading. We’ll see how they simplify code.
Understand where and how to apply method overriding
Understand where and how to apply method overloading
Learn polymorphism by applying it in a practical scenario.
Taking up the OOP Essentials: Inheritance Byte is a prerequisite as the current Byte builds on top of the codebase used there.
Polymorphism is about being able to change the functionality offered by a method, depending on the situation.
This is achieved in two ways
Method Overriding - Child class implements a method that is already present in the parent class, resulting in the child method being invoked - Dynamic polymorphism.
Method Overloading - Writing multiple methods with the same name but with different sets of parameters (or order of parameters) so that the appropriate method gets invoked based on the parameters passed - Static polymorphism. This may not be supported cleanly in some languages (e.g. Python).
In this Byte, we will learn method overriding and touch upon method overloading. We’ll see how they simplify code.
Understand where and how to apply method overriding
Understand where and how to apply method overloading
The company wants to continue improving the Messaging Application that we started with in the OOPS Inheritance Byte.
The Messaging Application now has an Inheritance based design to support multiple Message Types. There is a base class for Message
which is inherited by the child Message Type classes.
Each Message Type is now handled by different teams. The teams want more flexibility.
They don’t want to use the functionality provided by some of the methods in the base class. The teams want to implement their own functionality for some of these methods. They also don’t want to rename the methods since that would mean changing all the client code that is presently invoking that method.
Validation of the message contents
Checking if the message has offensive content
Each Message Type has a constructor that initializes their class variables to default values.
The teams want to have a way where some clients of this class can initialize the default values for some of these variables to a value of their choice.
Obvious way to achieve the first requirement would be for the different teams to introduce new methods in the child class that they’re managing.
Yeah, something like
class TextMessage(Message):
// other methods
// …
def is_valid_text_message(self):
if (self.get_message_size() > 100):
return True
else:
return False
class ImageMessage(Message):
// other methods
// …
def isValidImageMessage(self):
if (self.get_message_content() is None):
return False
else:
return True
We can see that the criteria set by the TextMessage team is that the message size to be at most 100 for it to be considered a valid message. The ImageMessage team is more moderate about the condition for an image message to be valid & is only checking if the message has any content.
As the methods were implemented independently by different teams, we can see a difference in the method names used.
If we were to check the client side implementation for accommodating these, we’ll need something like the below method inside oop_way_with_polymorphism/android_client_handler.py
def check_message_for_validity(message: Message):
if (message.get_message_type() is message.MSG_TYPE_TEXT):
message.is_valid_text_message()
elif (message.get_message_type() is message.MSG_TYPE_IMAGE):
message.isValidImageMessage()
Clients (e.g. android_client_handler.py) need to be aware of the method names used by each of the message types
Every time a new message type is introduced or teams want to have their own implementation, the Clients would need to make code changes.
A more OOPSie way to deal with the new requirement is to add a method with default implementation in the base class. That way, child classes can choose to use either the default method in the base class or "override" the original implementation in the base class with their own logic. The same method signature will be used by the child classes when they override.
This implementation is available inside the oop_way_with_polymorphism directory.
We can see the same requirement implemented by utilizing Polymorphism inside oop_way_with_polymorphism/message.py. Defined in it is the structure of is_valid()
method. The method is to be used by the child classes to check if a message is valid. Individual message type classes will have to override this method as no default implementation is provided by the base class.
class Message:
def is_valid(self):
raise NotImplementedError;
The TextMessage & ImageMessage teams now can use the same method structure to implement their own logic independently.
Even if the is_valid() method had some default implementation in the base class, it could be overridden in the child classes.
class TextMessage(Message):
// other methods
// …
def is_valid(self):
if (self.get_message_size() > 100):
return False
return True
class ImageMessage(Message):
// other methods
// …
def is_valid(self):
if (self.get_message_content() is None):
return False
return True
As any child class of Message
will be overriding the is_valid()
method to implement their own logic for the message’s validity, clients of these classes need not worry. Even with any new message types, the child class would inherit from the base class and implement this method. So, no change/addition would be required on the client side.
def check_message_for_validity(message: Message):
message.is_valid()
Easy for each message type class to have its own methods override the default base class functionality.
Clients are not impacted
As always we can run oop_way_with_polymorphism/main.py to see a demo of our functionality.
Oops!
We haven’t overridden the __str__()
method, right? This gets called when we try to print()
an object directly. Add your implementation of __str__()
for at least TextMessage
& ImageMessage
to run the command without errors. The method should return a string.
The second requirement is this.
Each Message Type has a constructor that initializes their class variables to default values.
The teams want to have a way where some clients of this class can initialize the default values for some of these variables to a value of their choice.
The teams can set the values explicitly using the set methods after the object is created.
message_1 = TextMessage()
message_1.set_sender('DEFAULT_SENDER')
message_1.set_receiver('DEFAULT_RECEIVER')
How about we utilize the constructor method which is already initializing the objects and pass these default values which we want to set for every object that is being created?
message_1 = TextMessage('DEFAULT_SENDER', 'DEFAULT_RECEIVER')
To handle this in the constructor of TextMessage and Message, we have to make these changes to both those constructors
class Message:
def __init__(self, sender=None, receiver=None):
# Common fields.
self.__message_id = None
self.__sender = sender
self.__receiver = receiver
self.__message_type = None
# Other code not shown here
class TextMessage(Message):
def __init__(self, sender=None, receiver=None):
super().__init__(sender, receiver)
self.__message_content = None
self.__message_type = Message.MSG_TYPE_TEXT
Here, we have overloaded the__init__()
methods to pass 2 new parameters. If those parameters are not passed, they will be initialized to None as seen in the parameter list passed to the__init__()
method.
Note: This form of method overloading is restrictive in Python since if you have two methods with the same name, only the second method definition holds good. The first one cannot be used.
In method overloading in C++ or Java, the original method as well as the new overloaded method can be invoked by passing the different input parameters needed by each method.
In a larger code base, the modification of a method by overloading in one place is preferred. For example, here, not adding those 2 additional lines to initialize the values every time is better. Also, it avoids two extra function calls.
Cleaner code.
Practical Scenarios
An add() method could add integers or decimals or strings or two objects of a class.
A display() method could call different overloaded methods depending on the parameter passed.
Operator overloading in languages
Summary of Polymorphism
Method overriding builds on top of inheritance.
Rules for overriding are
The functions should have the same name
Parameters should be same in number and type
The classes should be different for the overriding functions
Method overloading can be done within any class.
Rules for overloading are
Function names should be the same
Parameters should be different in numbers or in types
Functions should belong to the same class
Why is it useful? - presents the ability to perform different actions using the same method signature, keeping code cleaner.
Where to apply? - when child classes need to implement their own logic for methods provided by the parent class. Or when a class needs related methods to have the same method name with only a different set of parameters.
How to apply? - override/overload methods in the parent class from the child class. Language specific constructs apply.
What is the drawback if we don’t use polymorphism? - maintaining code will be harder as some of the changes would require corresponding changes in all of the dependent/client code.
Types of Polymorphism - Types of Polymorphism
Compile-time polymorphism (Static)
Run-time polymorphism (Dynamic)
+
operator supports different types of objects as its operands. Is this an example of inheritance or polymorphism?When is the call to an overloaded & overridden method resolved in Java, at compile-time or run-time? What does this mean for overriding static methods?
Does method overriding bring with it, performance issues?
What is the deal with Message Overloading not working in Python? Can you try it out to prove whether it works or not, compared to Java/C++?
Change behaviour of methods from parent class in the child class to suit the child class’ requirements
Use method overriding & overloading to implement polymorphism in your projects
Conquer these interview questions
What is Polymorphism?
How does method overriding & overloading differ?