Polymorphism in depth!

The article assumes the user to have some experience in Object Oriented Programming and uses Dart as a programming language for example.
Polymorphism in depth!

Article assumes user to have some experience in Object Oriented Programming. The article uses Dart as a programming language for example.

To understand polymorphism, let’s take an example of a Laptop.

Suppose you have a laptop with USB ports. What are the things that you can insert into the USB ports? The answer is anything that has a USB male connector.

Can it be a mouse? YES. Can it be a keyboard? YES. Can it be a printer? YES.

What’s common in these devices? They are I/O devices. They are used for input and output.

Now, programmers realized very quickly that it was of our interest to have our systems (computers) be independent of I/O devices.

Creation of this independence is called Polymorphism.

In our example USB was an interface between our laptop and the I/O devices.

Now, how do we do that in code ?Consider the following example :

Problem

Let’s say that module A calls module B .

We can consider module A to be our laptop and module B to be our keyboard.

In this scenario, the code will look as follows :

Here, ModuleA knows about ModuleB because the code in module_a.dart explicity has a declaration of ModuleB in relative line number 11.

The following will be the dependency graph :

This means ModuleA is tightly coupled to ModuleB in compile time.So, if ModuleA has to compile then it must have ModuleB compiled too. That's bad.

Why’s this bad ? Back in our example, this means that when our laptop was made it will only support a particular kind of keyboard designed for that laptop but we want any keyboard to work with our system.

Solution

To solve this, we create an interface between ModuleA and ModuleB so that ModuleA doesn't need to directly reference ModuleB . This can be done because ModuleA depends upon the function doSomething() present in ModuleB .

Now, In module_a.dart there is no declaration of ModuleB directly, instead it has a reference to some Interface .

Now, the dependency graph is like :

If we look at interface.dart we'll see that there is no implementation of the method doSomething() but ModuleA is able to call the function doSomething() in relative line number 4, in module_a.dart .

Notice, that ModuleB is no longer referenced in ModuleA anywhere in compile time but we can inject ModuleB into ModuleA at runtime via the constructor.

Why is this good ? It’s good because, this means that now we can have a ModuleX that implements the Interface which can work with ModuleA .

This gives the developer immense power.

This means that we can change the behavior of ModuleA without ever changing it's source code.

We can inject the dependencies as follows :

Here, ModuleA accepts ModuleX as well as ModuleB .

But, when we look at the output :

The behavior of ModuleA is different for the same function call.

This is why we like Polymorphism. It gives the developer immense power.