Blog/ Interface

If you are new to object-oriented programming, you might be confused about what an Interface is and how to use it. Read this post to clear that confusion.

Interfaces solve two problems:

They specify the methods that a class implementing an interface has to define. This makes for flexible architecture by facilitating swapping of one class with another without delving into implementation details.

They help child classes inherit methods from multiple parent classes. But you should never use it for this purpose. You will learn why and what to do instead.

Make architecture flexible and modular

Let's start with the example where we left off in the post on inheritance in PHP. We have two child classes: HondaAccord and ChevyRam, both extending the parent class Vehicle. All the methods and properties defined in the parent class are inherited by the child class. Child class can override these inherited properties and methods and add more of its own if it needs to. We all know that a person can buy and travel in his vehicle, be it Honda Accord or Chevy Ram. Let's create such a Person class in Person.php file.

In the above code, I am creating a person $you and a HondaAccord object $yourCar. Then I make you buy the car and let you travel in it for 2000 miles. At the end, I am asking for the number of miles traveled. Pretty simple! On executing the file Person.php, I get the following output:

$ php Person.php
You have traveled for 2000 miles in your Honda Accord.

In the above code, the only methods of the Vehicle class that are being used are drive() and getMilesDriven(). So if we replace the Vehicle object with any other object which has these two methods, and then let the user buy that object, the code will work without any problem. An example could be a bullock cart. Based on how we have described our Vehicle class, with warranty and color, bullock cart is definitely not a vehicle. But a person can still buy it, travel in it and then get the number of miles traveled in it. So for the Person class, bullock cart is a practical replacement for a vehicle. Now how does the Person class specify that it can buy and travel in any object that defines two methods: drive() and getMilesDriven()? That's where an interface comes in handy.

Using an interface, we can define the methods that any class implementing it should have. The implementing class can have more methods but it should at least have the methods that the interface defines otherwise PHP will throw an error. In our case, we can create VehicleInterface which has only two methods: drive() and getMilesDriven(). Vehicle and BullockCart classes will implement this interface, and Person class can buy and drive the class that implements it. Here is how the VehicleInterface looks in VehicleInterface.php file.

<?php
/**
* Interface VehicleInterface
*/
interface VehicleInterface {
/**
* Drive the vehicle. This will add to the miles driven.
*
* @param int $miles
* Number of miles driven.
*/
public function drive($miles);
/**
* Return the number of miles driven.
*
* @return int
* Number of miles driven.
*/
public function getMilesDriven();
}
?>

You can see in lines 45-55 above that I bought a bullock cart and traveled in it for 5000 miles (poor bullock!) using almost the same code as you bought a Honda Accord and traveled in it for 2000 miles. Person class, as a result, is no longer dependent on the Vehicle class. In fact, it can work with any class that implements the VehicleInterface. This adds a lot of flexibility to our code and makes the architecture modular. Interfaces, combined with Dependency Injection, make it very easy to replace one class by another without making much changes in the code. In real life, a simple example is interacting with the database. MySQL class, Postgres class, Oracle class can all implement the DatabaseInterface which provides a few methods such as select(), insert(), etc. Now you can write your application without worrying about what database it is using as long as your application code uses the methods specified in the interface. If in future, MySQL in your application gets replaced by Oracle, it's as easy as initializing Oracle class instead of MySQL and using it without any further change in your code.

Circumvent PHP's limitation of child class being able to extend only one parent class

At the top of the post, we had mentioned that the second use of interface is circumventing the limitation in PHP that a child class can inherit only one parent class. As you already know, HondaAccord has two methods: putInTrunk() and takeFromTrunk() that ChevyRam does not. There are other objects which are not vehicles but have a trunk and you can put something in their trunk and take something out. A simple example is any container, such as a suitecase (which is sort of a trunk itself). You can put something in it and later take something out. So theoretically we can define a Container class with a property $stuff and two methods: putInTrunk() and takeFromTrunk(). Then we can make HondaAccord a child of Container class, and it will inherit these methods. But that's the rub. HondaAccord already extends the Vehicle class. Since PHP does not allow a child class to extend two parent classes, it can't extend Container class as well. Instead we can define a ContainerInterface with two methods: putInTrunk() and takeFromTrunk(). Container class will implement these methods and so will HondaAccord class. Once HondaAccord implements ContainerInterface, we can use it anywhere ContainerInterface can be used. Here is how ContainerInterface, Container class and HondaAccord class will look:

Note that although in PHP, a child class can extend only one parent class, there is no such limitation on the number of interfaces a class can implement. A class can implement 1, 2 or even 100 interfaces. Once you make a class implement an interface, you can use it anywhere that interface is being used.

If you are of the opinion that the above code looks ugly, then you are correct. After all, Honda Accord and a container are not really related. We are putting them under the same interface just because both have a trunk, which we really shouldn't have to do. That's where Traits come in, which we'll discuss in the next post.

If you learned something from this post, please show your appreciation by commenting below. If you got stuck somewhere or found something difficult to understand, definitely let us know so that we can make that part of the post easier to understand.