Subtype Polymorphism – Swapping Implementation at Runtime

Share this article

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. At its most basic level, the aforementioned contract looks like this:
<?php
namespace LibraryCache;

interface CacheInterface
{
    public function set($id, $data);
    public function get($id);
    public function delete($id);
    public function exists($id);
}
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. Here’s the file-based cache implementation:
<?php
namespace LibraryCache;

class FileCache implements CacheInterface
{
    const DEFAULT_CACHE_DIRECTORY = 'Cache/';
    private $cacheDir;

    public function __construct($cacheDir = self::DEFAULT_CACHE_DIRECTORY) {
        $this->setCacheDir($cacheDir);
    }
    
    public function setCacheDir($cacheDir) {
        if (!is_dir($cacheDir)) {
            if (!mkdir($cacheDir, 0644)) {
                throw InvalidArgumentException('The cache directory is invalid.');
            }
        }
        $this->cacheDir = $cacheDir;
        return $this;
    }
    
    public function set($id, $data) {
        if (!file_put_contents($this->cacheDir . $id, serialize($data), LOCK_EX)) {
            throw new RuntimeException("Unable to cache the data with ID '$id'.");
        }
        return $this;
    }
    
    public function get($id) {
        if (!$data = unserialize(@file_get_contents($this->cacheDir . $id, false))) {
            throw new RuntimeException("Unable to get the data with ID '$id'.");
        }
        return $data;
    }
    
    public function delete($id) {
        if (!@unlink($this->cacheDir . $id)) {
            throw new RuntimeException("Unable to delete the data with ID '$id'.");
        }
        return $this;
    }
    
    public function exists($id) {
        return file_exists($this->cacheDir . $id);
    }
}
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:
<?php
namespace LibraryCache;

class ApcCache implements CacheInterface
{
    public function set($id, $data, $lifeTime = 0) {
        if (!apc_store($id, $data, (int) $lifeTime)) {
            throw new RuntimeException("Unable to cache the data with ID '$id'.");
        }
    }
    
    public function get($id) {
        if (!$data = apc_fetch($id)) {
            throw new RuntimeException("Unable to get the data with ID '$id'.");
        } 
        return $data;
    }
    
    public function delete($id) {
        if (!apc_delete($id)) {
            throw new RuntimeException("Unable to delete the data with ID '$id'.");
        }
    }
    
    public function exists($id) {
        return apc_exists($id);
    }
}
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:
<?php
namespace LibraryCache;

abstract class AbstractCache
{
    abstract public function set($id, $data);
    abstract public function get($id);
    abstract public function delete($id);
    abstract public function exists($id);
}
<?php
namespace LibraryCache;

class FileCache extends AbstractCache
{
	// the same implementation goes here
}
<?php
namespace LibraryCache;

class ApcCache extends AbstractCache
{
	// the same implementation goes here 
}
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:
<?php
namespace LibraryView;

interface ViewInterface
{
    public function setTemplate($template);
    public function __set($field, $value);
    public function __get($field);
    public function render();
}
<?php
namespace LibraryView;
use LibraryCacheCacheInterface;

class View implements ViewInterface
{
    const DEFAULT_TEMPLATE = 'default';    
    private $template;
    private $fields = array();
    private $cache;

    public function __construct(CacheInterface $cache, $template = self::DEFAULT_TEMPLATE) {
        $this->cache = $cache;
        $this->setTemplate($template);
    }
    
    public function setTemplate($template) {
        $template = $template . '.php';
        if (!is_file($template) || !is_readable($template)) {
            throw new InvalidArgumentException(
                "The template '$template' is invalid.");   
        }
        $this->template = $template;
        return $this;
    }
    
    public function __set($name, $value) {
        $this->fields[$name] = $value;
        return $this;
    }
    
    public function __get($name) {
        if (!isset($this->fields[$name])) {
            throw new InvalidArgumentException(
                "Unable to get the field '$field'.");
        }
        return $this->fields[$name];
    }
    
    public function render() {
        try {
            if (!$this->cache->exists($this->template)) {
                extract($this->fields);
                ob_start();
                include $this->template;
                $this->cache->set($this->template, ob_get_clean());
            }
            return $this->cache->get($this->template);
        }
        catch (RuntimeException $e) {
            throw new Exception($e->getMessage());
        } 
    }
}
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. Let’s say the view’s default template has the following structure:
<!doctype html>
<html>
 <head>
  <meta charset="utf-8">
  <title>The Default Template</title>
 </head>
 <body>
  <header>
   <h1>You are viewing the default page!</h1>
<?php echo $this->header;?>
  </header>
  <section>
<?php echo $this->body;?>
  </section>
  <footer>
<?php echo $this->footer;?>
  </footer>
 </body>
</html>
Now, let’s have some fun and cache the document by feeding the view with an instance of the ApcCache class:
<?php
use LibraryLoaderAutoloader,
    LibraryCacheFileCache,
    LibraryCacheApcCache,
    LibraryViewView;
        
require_once __DIR__ . '/Library/Loader/Autoloader.php';
$autoloader = new Autoloader;
$autoloader->register();

$view = new View(new ApcCache());

$view->header = 'This is my fancy header section';
$view->body = 'This is my fancy body section';
$view->footer = 'This is my fancy footer section';

echo $view->render();
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! Image via Fotolia

Frequently Asked Questions (FAQs) about Subtype Polymorphism

What is the main difference between subtype polymorphism and parametric polymorphism?

Subtype polymorphism, also known as inclusion polymorphism, is a form of polymorphism where a name denotes instances of many different classes related by some common superclass. On the other hand, parametric polymorphism allows a function or a data type to handle values identically without depending on their type. Parametric polymorphism is a way to make a language more expressive while still maintaining full static type-safety.

How does subtype polymorphism work in Java?

In Java, subtype polymorphism is implemented through the use of inheritance and interfaces. A superclass reference variable can point to a subclass object. This allows Java to make decisions at runtime about which method to call, which is known as dynamic method dispatch. It’s one of the powerful features of Java that enables it to support dynamic polymorphism.

Can you provide an example of subtype polymorphism?

Sure, let’s consider a simple example in Java. Suppose we have a superclass called ‘Animal’ and two subclasses ‘Dog’ and ‘Cat’. Both ‘Dog’ and ‘Cat’ classes override the ‘sound’ method of the ‘Animal’ class. Now, if we create an ‘Animal’ reference to a ‘Dog’ or ‘Cat’ object and call the ‘sound’ method, Java will decide at runtime which class’s ‘sound’ method to call. This is an example of subtype polymorphism.

What is the significance of subtype polymorphism in programming?

Subtype polymorphism is a fundamental aspect of object-oriented programming. It allows for flexibility and reusability of code. With subtype polymorphism, you can design a generic interface for a group of classes and then use this interface to interact with objects of these classes in a uniform way. This leads to cleaner, more intuitive, and maintainable code.

How does subtype polymorphism relate to the Liskov Substitution Principle?

The Liskov Substitution Principle (LSP) is a principle of object-oriented design that states that if a program is using a base class, it should be able to use any of its subclasses without the program knowing it. In other words, the objects of a superclass shall be replaceable with objects of a subclass without affecting the correctness of the program. Subtype polymorphism is a direct application of the LSP.

Is subtype polymorphism supported in all programming languages?

No, not all programming languages support subtype polymorphism. It’s primarily a feature of statically typed, object-oriented programming languages like Java, C++, and C#. Dynamically typed languages like Python and JavaScript have a different form of polymorphism known as duck typing.

What is the difference between static and dynamic polymorphism?

Static polymorphism, also known as compile-time polymorphism, is achieved through method overloading. The decision about which method to call is made at compile time. On the other hand, dynamic polymorphism, also known as runtime polymorphism, is achieved through method overriding. The decision about which method to call is made at runtime. Subtype polymorphism is a form of dynamic polymorphism.

Can you explain the concept of upcasting in subtype polymorphism?

Upcasting is the process of treating a derived class object as a base class object. It’s a key aspect of subtype polymorphism. When you upcast a derived class object, you can call any method that’s defined in the base class. However, if that method is overridden in the derived class, the overridden version will be called.

What is downcasting in the context of subtype polymorphism?

Downcasting is the opposite of upcasting. It’s the process of casting an object of a superclass to a subclass. Downcasting is used when you need to access methods that are only present in the subclass. However, downcasting can be risky because it can lead to a ClassCastException if the object being cast doesn’t actually have the type you’re casting to.

How does subtype polymorphism contribute to code reusability?

Subtype polymorphism allows us to write more generic and reusable code. By using a superclass reference to interact with subclass objects, we can write code that works with a wide range of objects, as long as they all belong to subclasses of the same superclass. This means we can add new subclasses without having to change the code that uses the superclass, which makes our code more flexible and easier to maintain.

Alejandro GervasioAlejandro Gervasio
View Author

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.

Advanced
Share this article
Read Next
Get the freshest news and resources for developers, designers and digital creators in your inbox each week