[C#] Polymorphism – Three Pillars of OOP (3)

This is the third article of the Three Pillars of OOP. This time, let’s look at the third pillar – Polymorphism.

 

1. What is Polymorphism?

Polymorphism – what a name? Poly means “many” and morph means “form” or “shape“. So the “Polymorphism” means “many forms”. In OO, Polymorphism allows a base class to define a set of methods that can be used by its derived classes. Users are exposed only to the base class’s public interfaces but during the runtime, the appropriate implementation of a derived class will be executed.

I know it’s still confusing. Let me explain with an example.

You are writing a text document and want to save it as several different formats such as .txt, .xml, .csv (comma separated version), etc.

The “Document” class is a base class and has a public “Save()” method. This “Save()” method is actually abstract because the generic “Document” class does not know about the output format.

For each document type, a new class is declared based on the “Document” class. “TextDocument“, “XmlDocument“, and “CsvDocument” classes inherit from the common base class “Document“. Each sub class implements its specific version of “Save()” the method.

Let’s look at the code snippet.

public abstract class Document
{
    public abstract void Save();
}
public class TextDocument : Document
{
    public override void Save() { }
}

Document myDoc = new TextDocument();
myDoc.Save();

Nothing special and impressive at all!!! We can do this in the Document class using the “if” statement or the “switch” statement. You may wonder what’s all this fuss about.

Now it’s time to face it. “Welcome to the world of OO !!!” Instead of “if” and “switch” statements, OO spreads its logic throughout multiple classes. Polymorphism works on the same philosophy.

In my opinion, Polymorphism is the heart of OO. It contains a lot of features what OO becomes really like OO.

 

2. Virtual and Override

Polymorphism provides a way for a subclass to define its own implementation of a method defined by its base class. This process is called “overriding.”

  • Mark the base class’s method  as “virtual
  • Mark the sub class’s method as “override
public abstract class Document
{
    public virtual void Save() { }
}
public class TextDocument : Document
{
    public override void Save() { }
}

 

3. Abstract methods

In some cases, you have no idea how to implement base class’s methods. In that case, you can mark the methods as abstract. Abstract methods do not need to have a body. Also if you mark a method as abstract, you also need to mark a class as abstract too. An abstract method is implicitly “virtual” because it must be overridden in the inheritance hierarchy.

public abstract class Document
{
    public abstract void Save(); // no implementation
}
public class TextDocument : Document
{
    public override void Save() { ... } // implemented here
}

 

4. Assign a reference to a derived class to the base class’s reference variable

Let’s look at the following two lines of code:

Animal lion = new Lion();
Lion lion = new Animal();

Which one makes sense to you? A lion is an animal. But an animal is a lion? Maybe not.

It’s always OK to assign a subclass’s reference to a variable of a base class, but the opposite is not true. This feature makes Polymorphism work in a real sense.

 

5. Determine what to execute at runtime

Now it’s time to see how Polymorphism looks like.

Document[] myDocs = new Document[3];

myDocs[0] = new TextDocument();
myDocs[1] = new XmlDocument();
myDocs[2] = new CsvDocument();

foreach(Document doc in myDocs)
{
   doc.Save();
}

Text, Xml, and Csv are all documents. The Document Type variable can hold any type of object as long as it is a subclass of the “Document” class. Whenever you call “Save()” method, the runtime determines which “Save()” method should be called according to the underlying type of an object.

That’s it. This behavior is called Polymorphism. With polymorphism, you can group related objects together and perform the task without thinking about the underlying types.

 

6. Programming against an Interface not a Class

Programming against an Interface” is a maxim in the OO world. What does this exactly mean, though?

When you design your application, you will create a model with a lot of objects and their relationships. You are going to define classes and connect them using “is-a” or “has-a” relationships.

You can create each object specifically with its own type and use them. But encapsulation gives you insight in this bigger picture too. Rather than taking care of each class one by one, you can create a set of public behaviors for related classes.

An abstract class is a good example. You are not allowed to create an object of an abstract class directly. It just exposes the method signature. But you can create a variable with an abstract class type and assign an object of the subclass of it to the variable. Subsequent calls from the variable actually executes the method in the subclass through Polymorphism.

One problem of this abstract class approach is that C# only allow one base class. Suppose your design has 7 classes; Animal, Bird, Mammal, Lion, Eagle, Penguin, and Bat.

class Animal {}
class Bird:Animal {}
class Mammal:Animal{}
class Eagle:Bird {}
class Penguin:Bird {}
class Lion:Mammal {}
class Bat:Mammal {}

Nicely grouped. Now you want to add a fly() method in this Animal chain. At first, the fly() method is added to the Bird class. The Eagle can fly automatically. But what about Penguins? They can’t fly. Moreover, bats can fly even though they are mammals.

If you are using C++, you can create a “Flyable” class, which has a “fly()” method and make Eagle and Bat inherit from the “Flyable” class. Not in C#.

The solution of this problem in C# is “Interfaces“. Classes can inherit from multiple interfaces.

An interface has the following properties:

  • An interface cannot be instantiated directly
  • Interfaces contain no implementation of methods
  • Classes and structs can implement more than one interface
  • An interface itself can inherit from multiple interfaces.
class Animal {}
class Bird: Animal {}
class Mammal: Animal{}
interface IFlyable { void fly(); }
class Eagle: Bird, IFlyable {}
class Penguin: Bird {}
class Lion: Mammal {}
class Bat: Mammal, IFlyable {}

 

7. Overriding a base method vs Hiding a base method

What is the point of virtual/override keywords? Let’s experiment this.

  • Case 1: virtual/override
public class Animal
{
    public virtual void Eat()
    {
        Console.WriteLine("Can't eat");
    }
}

public class Tiger : Animal
{
    public override void Eat()
    {
        Console.WriteLine("I am eating a deer.");
    }
}
Animal tiger = new Tiger();
tiger.Eat();

This will print the “I am eating a deer.” message on the console. Even though the type of the variable is “Animal”, the underlying type is “Tiger”  and the runtime will execute the Tiger’s Eat() method through Polymorphism.

  • Case 2 – only override

If you try override the method which is not virtual, you will get an compile time error.

  • Case 3 – only virtual
public class Animal
{
    public virtual void Eat()
    {
        Console.WriteLine("Can't eat");
    }
}

public class Tiger : Animal
{
    public void Eat()
    {
        Console.WriteLine("I am eating a deer.");
    }
}

Animal tiger1 = new Tiger();
tiger1.Eat();

Now the non-overridden version of the “Eat()” method in the sub class is provided. What will happen?

The output is “Can’t eat”. Without the “override” keyword, the Polymorphism does not work. The runtime will run the “Eat” method of the variable type (Animal).

Let’s change the code a little bit.

Tiger tiger2 = new Tiger();
tiger2.Eat();

Now the result is “I am eating a deer.” on the screen.

The “virtual” keyword is a hint. It says this method might be overridden in the subclass or not. It is up to you to override it or not. If you do not override it, the runtime calls the method of the variable’s type.

  • Case 4 – no virtual/ no override

What if we just define the exactly the same method both in a base class and a subclass? The result is the same as the case 3.

 

8. Example – Polymorphism using an abstract class

public abstract class Document
{
    public abstract void Save();
    public static Document CreateDocument(string type)
    {
        Document doc;
        switch (type)
        {
            case "txt": doc = new TextDocument(); break;
            case "xml": doc = new XmlDocument(); break;
            case "csv": doc = new CsvDocument(); break;
            default: doc = null; break;
        }
        return doc;
    }
}

class TextDocument : Document
{
    public override void Save()
    {
        Console.WriteLine("Saving a Text file");
    }
}

class XmlDocument : Document
{
    public override void Save()
    {
        Console.WriteLine("Saving an Xml file");
    }
}

class CsvDocument : Document
{
    public override void Save()
    {
        Console.WriteLine("Saving a Csv file");
    }
}

Document[] myDocs = new Document[3];
myDocs[0] = Document.CreateDocument("txt");
myDocs[1] = Document.CreateDocument("xml");
myDocs[2] = Document.CreateDocument("csv");

foreach(Document doc in myDocs)
{
    doc.Save();
}

The output is:
Saving a Text file
Saving an Xml file
Saving a Csv file

In most cases, even subclasses are encapsulated. The Document class provides a factory method to create an instance. (This is one of the most popular design patterns) You do not even know which sub class you need to instantiate. Just pass an argument and an appropriate instance will be created and returned to you. Once you get a Document object, the underlying type does not matter any more. Every document is just a document.

 

9. Example – Polymorphism using an interface

public class Animal
{
    public virtual void Eat()
    {
        Console.WriteLine("Can't eat");
    }
}
class Bird:Animal {}
class Mammal:Animal{}

interface IFlyable
{
    void fly();
}

class Eagle: Bird, IFlyable
{
    public override void Eat()
    {
        Console.WriteLine("I am eating rotten meat.");
    }
    public void fly()
    {
        Console.WriteLine("I am flying high.");
    }
}

class Penguin: Bird
{
    public override void Eat()
    {
        Console.WriteLine("I am eating fish.");
    }
}

class Lion: Mammal
{
    public override void Eat()
    {
        Console.WriteLine("I am eating a deer.");
    }
}

class Bat: Mammal, IFlyable
{
    public override void Eat()
    {
        Console.WriteLine("I am sucking blood.");
    }
    public void fly()
    {
        Console.WriteLine("Am I flying?");
    }
}

IFlyable[] flyingAnimals = { new Eagle(), new Bat() };
foreach (IFlyable f in flyingAnimals)
{
    f.fly();
}

One thought on “[C#] Polymorphism – Three Pillars of OOP (3)

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s