Missing Opportunities for Polymorphism
It is not without irony that one of the best examples of polymorphism is also a sign that you are missing opportunities to exploit polymorphism. The example is best expressed in Smalltalk where the if-then-else control structure is implemented as method call. If you send an if: ablock else: anotherBlock message to true, it's implementation of if:else: will execute ablock. Sending that same message to false will cause anotherBlock to be executed. This side by side implementation is demonstrated in listing 1.
true false if: aBlock else: anotherBlock if: aBlock else: anotherBlock
^aBlock execute. ^anotherBlock execute.
Listing 1. Smalltalk's polymorphic implementation of if:else:
By unifying the API for these booleans, we can write code such as "result := someBoolean if: aBlock else: anotherBlock." and end up with the expected results. However, the fact that we had to execute and if-then-else in the first place was because at some point in the past we lost or have not properly captured some vital piece of information. Now that this bit of information is missing, we have to recreate that information and the tool that is most commonly used to do that is if-then-else.
How does this relate to polymorphism? One of the beautiful features of capturing state and behavior into an object is that these objects act as mini execution contexts. If we revisit the code in listing 1, we can see that we don't have to make any decisions. We can use the fact that our execution context is either true or false allows us to get straight to the block that is to executed. If we didn't completely understand the context in which we were working in, we'd be forced to some how rebuild that context in order for us to know what to do.
Lets consider the case where we are modeling bank accounts. Now we could define all accounts as just that, Account. We could distinguish between a checking and savings account an attribute (enum?), accountType. I could also capture the distinction by creating two classes, CheckingAccount and SavingsAccount. Lets say we now implement an accrueInterest() method where each type of account has a different interest rate. With the former case we will have to write some logic to match the type of account with the rate of interest. With the later, each object knows what it is and hence can just directly acquire the interest rate. If can do this because of how it is defined without using if-then-else to rebuild that information.
Classes designed to exploit polymorphism have a much reduced dependency on if-then-else. They also tend to be more readable and contain less code. Each of these attributes show that improved polymorphism is more than just a neat coding trick, it's a technique that leads to better code.