Software Design Patterns

Polymorphism

Polymorphism

Polymorphism: Many forms.

Not only does 'polymorphism' mean 'many forms, but there are many forms of polymorphism. It breaks down into 2 main catagories each having 2 catagories. The tree looks like this.

Polymorphism
Ad hoc Polymorphism Universal Polymorphism
Coercion Polymorphism Overloading Polymorphism Parametric Polymorphism Inclusion Polymorphism

As OOP programmers, we are most interested in inclusion polymorphism which is also called subtype polymorphism. When we have objects that derive from a common interface, we are using inclusing (subtype) polymorphism.

Why do we care about polymorphism:
One one of our main design principles of OOP, "program to interfaces, not implementations", means in essence that we are going to build interfaces and then subclass those interfaces to carry out more specific tasks. We are going to use the base type as much as possible in client code to avoid getting stuck with being tightly coupled to a single class.

Confused? Ok, lets say we have a class that drives car. Some cars are automatics, some are straight drives, some straight drives have shifters on the floor, and some have shifters on the steering columns (old trucks had these).

So we could have an interface like this:

class Car
{
    protected Start()
   {
    TurnKeyOn();
   }
   virtual ShiftGears(); //implemented in subtypes
   protected TurnOff()
   {
    TurnKeyOff();
   }
}

Note that when I say interface this is an interface or a virtual class. The main point is that it can not be instantiated directly and must be derived from and the subtypes can be instantiated.

Sure it should be much more complex than that, but we'll keep it really simple.

We have many different types of cars and we know that they will have different ways to shift, so this is simple right? We'll just inherit from 'Car' and override ShiftGears() in the different types of cars, like so:

class Camry_Manual : Car
{
  ShiftGears(){
    //code to shift on floor shifter
  }
}

class Camry_Auto : Car
{
  ShiftGears(){
    //do nothing, its automatic
  }
}

class OldTruck_Manual : Car
{
  ShiftGears(){
    //shift on column
  }
}

Now we have our program that drives cars and it has to shift gears as it drives so with the 'magic' of polymorphism, we can have one function that can drive all kinds of cars like this:

Car aCar = new Camry_Auto();
Car bCar = new Camry_Manual();
Car cCar = new OldTruck_Manual();

DriveCar(aCar);
DriveCar(bCar);
DriveCar(cCar);

...

function DriveCar(Car myCar)
{
  //other code to drive car
  myCar.ShiftGears();
  //..more code to continue driving
}

When we call 'ShiftGears()' on a Car, we are using polymorphism to deal with the code in our derived classes even though the function was not expecting our derived type explicitly. This code will execute the 'ShiftGears()' function in the derived object that was instantiated (Camry_Auto, Camry_Manual, or OldTruck_Manual) because the function was overriden there. Because our client code ( DriveCar(Car myCar)  function) is working with an interface (Car) instead of a specific implemtation, we get more reuse from our client code and we can add more car types and not have to change the 'DriveCar()' function.

Our 'DriveCar()' function is NOT tightly coupled to specific types of cars, which is good. We are striving for loose coupling, high cohesion and this is a step in the right direction.

Great, huh? Actually this code still has big problems because we are having to write the 'ShiftGears()' function for every type of car so we are not getting any reuse of code at the object level which is the point of OOP right? We'll fix this with a pattern called the Strategy Pattern.

More on Polymorphism...

You can subsitute different objects that have the required interface at runtime (dynamic binding).

Allows client to make few assumptions other than supporting the interface.

Decouples and lets relationships of objects vary at runtime.

[ 1/1/1980 ]

0 Comments