Subtype Polymorphism – Swapping Implementation at Runtime

How many people doubt the relevance of Inheritance and Polymorphism in Object-Oriented design? Probably very few, most likely because of ignorance or a narrow mindset. But there is one small gotcha here that can’t be easily overlooked. While understanding the logic that pulls on the reins of Inheritance is straightforward, things are more difficult when it comes to digging deep into the nuts and bolts of Polymorphism.

The term Polymorphism is intimidating in its own right, and its academic definition is plagued with several disparate slants which make it even harder to understand what’s actually under its hood. Peripheral concepts such as Parametric Polymorphism and ad-hoc Polymorphism, usually implemented through method overriding/overloading, do have prolific niches in some programming languages, but the last instance should be dropped when it comes to designing extensible systems capable of consuming specific contracts (read abstractions) without having to check if the implementers belong to an expected type.

In short, most of the time any generic reference made to Polymorphism in OOP is implicitly assumed as the ability exposed by a system for defining a set of contracts or interfaces which in turn are honored by different implementations. This kind of “canonical” Polymorphism is commonly known as subtype Polymorphism, as the implementers of the interfaces are considered subtypes of them regardless if there’s an actual hierarchy down the line or not.

As one might expect, understanding the nature of Polymorphism is only half of the learning process; the other half is, of course, demonstrating how to design polymorphic systems that can be accommodated to work in pretty realistic situations, without getting caught in the trap of showcasing just “some nifty didactical code” (in many cases a cheap euphemism for toy code).

In this article I’ll show you how to exploit the virtues that Polymorphism offers through the development of a pluggable cache component. The core functionality can be expanded later to suit your needs through the development of additional cache drivers.

Defining the Component’s Interfaces and Implementations

The menu of available options to choose from when building an extensible caching component is anything but scarce (if you’re skeptical, just peek behind the curtain of some popular frameworks). In this case in particular, though, the component I present here has the nifty ability to swap out different cache drivers at runtime, all without having to amend a single chunk of client code.

So how do we get this going without sweating too much during the development process? Well, the first logical step to take would be …yep, define a segregated cache contract which will be agreed to later by distinct implementations, hence taking advantage of the benefits of Polymorphism.

The CacheInterface interface is a skeletal contract that abstracts the behavior of a generic caching element. With the interface in place, it’s easy to create a few concrete cache implementations that adheres to the its contract.

Since I want to keep things concise and easy to assimilate, the cache drivers I set up will be just a skinny duet: the first one uses the file system as the underlying backend for caching/fetching data, while the second one employs the APC extension behind the scenes.

The driving logic of the FileCache class should be easy to understand. By far the most relevant thing here is that it exposes a neat polymorphic behavior as it’s a faithful implementer of the earlier CacheInterface.

Although this ability is sweet and charming, on its own it’s not precisely something I’d sing praise about considering that the goal here is to create a cache component capable of switching back-ends at runtime. Let’s make an extra effort on behalf of an instructive cause and bring to life yet another slim implementer of the CacheInterface.

The implementation below adheres to the interface’s contract, but this time by consuming methods bundled with the APC extension:

The ApcCache class isn’t the flashiest APC wrapper you’ll see in your career, it packages all the functionality required for saving, retrieving, and removing data from memory.

Let’s pat ourselves on the back as we’ve managed to implement a lightweight cache module whose concrete back-ends not only can be swapped out easily at runtime thanks to their polymorphic nature, but adding a few more down the road is ridiculously simple. Just write another implementation that adheres to the CacheInterface.

I should stress however that actual subtype Polymorphism has been achieved by implementing a contract defined through an interface construct, which is a pretty ubiquitous approach. Nothing should stop you, however, from being less orthodox and getting the same results by switching over an interface declared as a set of abstract methods pitched inside an abstract class. If you’re feeling adventurous and want to walk that sideways path, the contract and the corresponding implementations could be refactored as follows:

From top to bottom, this is true a polymorphic approach that fights head to head with the one discussed before. Personally, and this is nothing but speaking for myself, I prefer to use interface constructs for defining contracts and appeal to abstract classes only when it comes to encapsulating a boilerplate implementation shared by a few subtypes. It’s up to you to pick the methodology that will best fit your needs.

At this point I could drop the curtain write some fancy closing comments, babbling on about our impressive coding skills and bragging about the flexibility of or cache component, but that would be plain a disservice. Polymorphism exposes its most seductive facet when there exists some client code capable of consuming several implementations without checking if they’re types of something as long as they adhere to an expected contract. So, let’s unveil that facet by hooking up the cache component to a basic client view class, something that will permit us do some neat HTML caching without much fuss.

Putting the Cache Drivers to Work

Caching HTML output through our sample cache module is so banal, that I’ll save any verbose explanation for some other time. The whole caching process can be reduced to a naïve view class, similar to this one:

The flashiest kid on the block is the class’ constructor, which consumes an implementer of the earlier CacheInterface, and the render() method. Since the responsibility of this last one is caching the view’s template once it’s been pushed through the output buffer, it’d be pretty nice to exploit this ability and cache an entire HTML document.

Pretty nice, right? But hang on just a moment! I got so caught up in the moment that I forgot to mention the above snippet will blow up on any system where the APC extension hasn’t been installed (naughty sysadmins!). Does that mean that the beautifully-crafted cache module isn’t reusable anymore?

Here’s precisely where the file-based driver comes to the rescue, which can be dropped into client code without getting a single compliant from it:

<?php
$view = new View(new FileCache());

The above one-liner explicitly declares that the view will be caching its output using the file system instead of shared memory. This switching of cache back-ends on the fly shows in a nutshell why Polymorphism is so crucial when it comes to designing highly-decoupled modules. It allows us to easily rewire things at runtime without rippling fragility/rigidity-related artifacts to other parts of our system.

Closing Remarks

Crushed under a overwhelming pile of formal definitions that make understanding the concept elusive, Polymorphism is really one of those beautiful things in life which, once you do understand it, makes you wonder how you could have gone on for so long without it.

Polymorphic systems are by nature much more orthogonal, easier to extend, and a lot less vulnerable to infringing central paradigms such as the Open/Closed Principle and the wise “Programming to Interfaces” mantra. Although rather primitive, our cache module is a shining example of these virtues in action.

If you haven’t refactored your applications yet so that they can take advantage of the benefits brought to the table by Polymorphism, you’d better hurry up because you’re missing the jackpot!

Alejandro Gervasio is a senior System Analyst from Argentina who has been involved in software development since the mid-80's. He has more than 12 years of experience in PHP development, 10 years in Java Programming, Object-Oriented Design, and most of the client-side technologies available out there.