Interface vs Abstract class in PHP

Interfaces and Abstract classes are powerful and fundamental concepts in OOP, but there’s an awful lot of confusion about what these do and why you should use each.

We will examine why an abstract class is good when you want to specify some implementation details, and how an interface is good when you can’t generically describe any of the implementation details but you know what the functions should be named and what their signatures should look like.

Let’s take a look at why these should be utilized with some simple analogies and code examples.

Coding a Zoo

Building a zoo application is a great environment to make sense of how an abstract class might be useful for a program because animals have so many common features and functions, but can also be entirely different. Consider a reptile cage in a zoo. In our program, we want our reptiles to be able to do some basic things:

  • Blink
  • Eat

Let’s think about how a couple of reptiles might practically do these things, specifically a lizard and a snake:

  • A lizard and a snake both blink their eyes in the same way, by closing then opening their eyes. This is the same for both.
  • A lizard eats by a pretty standard mechanism. A snake on the other hand kind of pulls food into its body whole and doesn’t chew or swallow.

If we were to design this without an abstract class, we could simply make a parent class called “Animal”

<?php
class Animal {

public function blink() {
  echo "close eyes";
  echo "open eyes";
}

public function eat() {
  ... there's nothing we can really put here ...
}

There’s no good way to define the eat function with a regular parent class. This is exactly the scenario in which PHP’s abstract class becomes powerful. We can instead declare Animal an abstract class, and an abstract function called eat, which won’t specify implementation.

<?php
class Animal {
...
abstract public function eat();
}

There are a couple of things that this guarantees in our zoo application.

  1. A programmer can’t create an instance of the class Animal.
  2. A programmer can’t extend class Animal unless they implement an eat function (sort of – I’ll discuss that later).

Characteristics of Abstract classes in PHP

There are some important things to realize about abstract classes in PHP. First, you cannot create an instance of them. In our zoo example, this keeps a programmer from writing something like

$animal = new Animal();

What would that class be? You don’t want a programmer to create a generic “animal”, since there is no actual representation of an animal in the zoo. Animal is an abstract representation of what an animal should be like. In order to have an animal, the programmer will have to create an instance of a real world class like

$snake = new Snake();
$lizard = new Lizard();

Which will create an instance of a snake or a lizard – both of which will have animal features and functions like our abstract class specifies.

Another huge benefit for our zoo is how abstract functions must be implemented by an extending class (unless it is also abstract). This means that our code in the abstract class:

abstract class Animal {
...
abstract public function eat();

requires that a class extending class Animal must have its own function called eat like in the example below

class Snake {
  public function eat() {
    /* first, unhinge your jaw
       while there is food outside
       pull it into your weird body
       when its all in there, close
       your mouth and digest away */
  }
}

class Lizard {
  public function eat() {
    /* Open your mouth
       put food into it
       chew
       swallow like a normal animal */
  }
}

The snake and lizard classes implement the eat function very differently but a developer is ensured that they both have a function called eat. In the case where eat was not defined as an abstract function at all, we wouldn’t know how to make each animal consume food. The method name could be different, or it could have been forgotten entirely. In most cases you can dig through source code to find out what type of object you are dealing with, but what if you can’t be sure if you are operating upon a certain type of animal?

function getReptiles() {
    $feeding_cage = array();
    for($k=0;$k<5;$k++) {
        $i = rand(0, 1);
        if ($i == 0) {
            array_push($feeding_cage, new Snake());
        } else {
            array_push($feeding_cage, new Lizard());
        }
    }
    return $feeding_cage;
}

This function generates a random array of snakes and lizards and places them into an array called $feeding_cage. Now say we want each reptile to eat. How would we do it if we weren’t sure there was an eat function? We’d have to check the type of reptile and then make sure we call the correct function or perform the correct operations. If we have a sort of contract through an abstract class, we can be sure that all of those reptiles extending classes have some sort of eat function.

foreach($feeding_cage as $reptile) {
  $reptile->eat();
}

//does something like 
*snake is eating weirdly*
*lizard is eating okay*
*lizard is eating okay*
*snake is eating weirdly*
*lizard is eating okay*

We know this will work fine because we must implement an eat function in order to extend the class Animal.

Using an interface

So far in our practical example, there is nothing that we have done with an abstract class that we can’t do with an interface.

interface Animal {
  public function eat();
}

The code is very similar – you just change a few keywords. So why not just use an interface? Remember our blink example and consider these characteristics of interfaces. Interfaces can’t implement functions, only specify their name and signature. This means if you specify a blink function in an interface, you can’t tell an implementing class how blink works. You are simply telling a programmer that they’d better write a blink function. Well, what if blink is the same for pretty much every animal you can think of? You’d have to implement a blink function with the exact same code in every class that implements the Animal interface.

class Snake implements Animal {
  function eat() {
    // do some gross snake stuff
  }
  function blink() {
    //close eyes
    //open eyes
  }
}
class Lizard implements Animal {
  function eat() {
    //do some different lizard stuff
  }
  function blink() {
    //close eyes
    //open eyes
  }
}

This is obviously a waste of code! We want to ensure that a programmer has access to eat and blink so we can do the following

foreach($feeding_cage as $reptile) {
  $reptile->eat();
  $reptile->blink();
}

//does something like 
*snake is eating weirdly*
  //blinks
*lizard is eating okay*
  //blinks
*lizard is eating okay*
  //blinks
*snake is eating weirdly*
  //blinks
*lizard is eating okay*
  //blinks

But we don’t want to have to rewrite the blink function in every single animal. This is a perfect time to use an abstract class because it allows you to specify some implementation details.

If you didn’t have any common implementations between your concrete classes but wanted to specify common function signatures that would be consistent across all implementing classes you would want to use an interface.

abstract class Animal {
  public function blink() {
    echo "blinks";
  }
  abstract function eat();
}

The above abstract class allows us to reuse the implementation of blink, and require the programmer to implement eat in the inheriting class. If there is no generic definition of any of your functions, you can simply use an interface.

Leave a Reply

Your email address will not be published. Required fields are marked *