Polymorphism and WordPress: Abstract classes

When we covered inheritance, there were some questions about interfaces and abstract classes.

  • What can you use them for?
  • What are the advantages?
  • When should you use them?

These are all great questions that are worth exploring. As the title suggests, “Polymorphism” is the object-oriented feature that helps answer these questions. It’s not an easy feature to grasp.

That’s why most of the article will be about an in-depth example. You’ll see the thought process involved with using it. This will help you understand it better so you can apply it to your own projects.

Many forms. Such polymorphism. Wow.

Polymorphism is all about a single idea. It’s that you can use a common interface or class to represent different types of objects.There are different types of polymorphism. The one that we care about is “Subtype polymorphism“. It’s usually the type of polymorphism referred to in object-oriented programming.

So what is polymorphism? So if you remember, inheritance is about creating “is-a” relationships between your classes. Well, polymorphism is all about those relationships. It’s about how you build your classes around it. It’s about harnessing those common elements between your classes.

When working with polymorphism, you ask questions like:

  • What does My_Widget class have in common with every other widget classes?
  • Is there a way I could reuse these common widget elements?
  • Are these common elements related to anything else?

These are WordPress related questions. That said, you can ask the same general questions regardless of the context. The goal is to identify what you need to extract and reuse between your classes. You’ll rely on the tools that you saw in the inheritance article to do this.

Copy/pasting is a great way to detect polymorphism

As programmers, we are notorious for copy/pasting code. You do it to learn from others, but you also do it to just to copy code between projects or even within projects.

If you find yourself copying code and altering a few lines, you might want to use polymorphism. Look through that code and ask yourself the same questions as earlier. You might see a pattern emerge.

This will help you deal with duplicate code and bug fixing (you only fix the bug once!). Your code is stronger as a result. You might even learn something about your code in the process.

Abstract classes: where the common code lives

So you found some common elements in your code. What do you do now? This is where abstract classes come in. You can put that code in one and then have your other classes inherit it.

An example: WordPress admin pages

Let’s go over a practical example to go over the thought process involved. Here are two classes that represent two different admin pages in WordPress. Below is an example based on the code in the single responsibility principle article.

Analyzing the common elements of both classes

Just by being admin pages, these two classes scream commonality. Let’s look at the code, what’s the same? What’s different? Each of you will have unique common elements to your plugin or theme. It’s possible that the elements that you see below won’t apply to you. That said, the thought process is the same for everyone.

Let’s start with what’s the same. Take a few seconds to think about it before continuing. Here is a rough list:

  • You need to register the admin page with WordPress
  • You register the same methods with WordPress
  • You need access to your plugin options
  • Your constructor is the same
  • You need to render form fields in both cases

Now, what’s different? Well, there are a few things.

  • The page title is different
  • The menu slug is different
  • Your forms are different and need different interactions with the settings API
  • Rendering these forms will be different

Creating an abstract class

With our analysis complete, let’s build an abstract class with that information. This abstract class represents the essence of what an admin page is about.

Note: The use of “new static” in the code above requires PHP 5.3.

Let’s go over some of the design decisions. As you learn more object-oriented concepts, these decisions become more complicated. They aren’t as clear cut because the problems are harder. That’s what object-oriented programming is about in the end.

That why it’s useful for you to see why and how those decisions happen. This will allow you to develop your own opinion and find your own solutions.

Registering and initializing each admin page

To solve this problem, we need three of the methods defined above. You use the register method which is our custom constructor. It calls the constructor and adds the common hooks. You also need to register the options page with WordPress.

A valid concern would be that you might not want to use get_option for each admin page. In that case, the constructor isn’t necessary. You could just use method overloading in the child class to load the options. Here is how it could look like.

Abstract methods

The class has four abstract methods defined: configure, render, get_menu_slug and get_page_title. The abstract class registered these methods with WordPress because every admin page needs them.

That said, each admin page doesn’t need these methods to do the same thing. They have their own unique title, menu slug, configuration and rendering.  That’s why we defined them as abstract using the abstract keyword.

Now, if you hadn’t noticed, these four methods have something in common. They represent what we analyzed to be different between the two classes. Abstract methods are one of the possible solutions to handling these non-common elements.

It works well in this example, but you might not always be able to do this. You might want to use overloading instead. Each situation is unique. This is where knowledge of the inheritance toolkit comes in handy.

render_form_field method

This is a helper method that any admin page can use. Helper methods like this are worth putting in the abstract classes. By doing that, any child class can leverage them. In this case, most admin pages need to render a form field.

It’s worth pointing out that you shouldn’t add every method into the abstract class. You want to take time and think about it. You don’t want your abstract class to act as a “catch all”. You’ll end up with a class that’s bloated and hard to work with.

As mentioned earlier, copy/pasting is a good way to test what you need to put in it. Are you copy/pasting code between two admin page classes? Take a good look at what you are copying. How can you alter it so you can reuse it elsewhere? Once you figure that out, that would go in your abstract class.

The child classes

Let’s take a look at the resulting child classes. They are smaller. Their code is clearer. That’s because you extracted the common code out of them. What you have left is code that focuses on the unique task that each class needs to do.

We’re entering design territory

The goal with these examples was to show you the thought process when using polymorphism. It’s about easing you into the idea of software design with objects. It’s how you use the tools under your belt to solve your unique problems.

Problems that don’t always have predefined solutions because each situation is unique. Sometimes there’s more than one way to do what you want to do.

An example about copy/paste

Here is a design decision example using the abstract class shown earlier. The first version of the class had an abstract method for add_admin_page and not render. Each admin page needs to add its own admin page and rendering methods. At first glance, it made sense.

When it came time to create the child classes, I was copying the same code over. I had add_admin_page, but I was only changing the page title. This raised a flag right away.

“What do I really need in my child class?”, I thought.

The answer was that I needed to render the page and I needed the page title/slug. So I altered the code to get the result you saw earlier. I added render, get_menu_slug and get_page_title as abstract methods. I replaced add_admin_page with a method using those three new abstract methods.

What about interfaces?

The initial plan was to have this article cover both interfaces and abstract classes. As the article grew, it became obvious that interfaces needed their own article. They need their own discussion, their own examples and their own conclusion.

We’ll cover them in a future article.