Composition over Inheritance [example in PHP]

Coding (Php 7.x)


Learn the difference between is-a and has-a relationship with examples that show why favor composition over inheritance
 
/img/blog/composition-over-inheritance-with-php-example.jpg

How we learn a programming language

 

The steps that you, me and everyone before us have taken to pursue the path to master programming looks more or less the same.

 

We start learning the basics, then slowly move to procedural programming, eventually we bravely jump in object-oriented building structures that include classes and methods,

 

Once we feel comfortable with basic OOP we then start to use abstract classes, interface and create relationships using the concept of inheritance.

 

It was by chance that I stumble upon a video that explained why inheritances were not highly regarded in the programming world, instead of more experienced web developers would opt for a new concept (for me) called composition.


The dictionary tells us that the verb composite is the act of making or form a whole by combining smaller things, parts, or elements.
 

In computer science composition is the process of combining different elements of our program to form a new object, we say that an object can be composite when it HAS determined peculiarity.

 

It has several advantages and if you take only one concept out of this post is to learn its efficiency and replace the majority of the inheritance in your code with it.

 

The principle of Composition over Inheritance

 

The video that changed my perspective was created by Mattias Johansson, creator of the YT channel Fun Fun Function and former JavaScript developer and Spotify.

 

In there he used JS to illustrate the various software design problems that may occur on a project that heavily uses inheritance.

 

In the script, there were two types of animals: dogs and cats then another class ‘robots’ was added to the program and it shared some but not all of the characteristics of the former.

 

After adding some new requirements it was easy to understand how the inheritance created issues in the reliability of the code whereas by favor a composition approach the example would run smoothly and effortlessly.

 

The reason is that Inheritance happens when you design your objects depending on what they are, instead, Composition is when you design them according to what they do.

 

In this post, you are going to see a similar problem, this time in PHP, and how Composition can help us solve it.

 


Difference between is-a relationship and has-a relationship

 

The two schools of thought regarding this concept of Composition over Inheritance have been a source of debate for several years now,

 

The reality is that there isn’t a real answer and, if there is, the solution is blurred.

 

(tip: If the concepts you have seen so far seems a bit too complicated you might want to brush up the basics of PHP)

 

For instance, cars and motorbike are vehicles which means they can inherit from the Vehicle class, it is also true that cars and motorbike have engines and wheels so than be composite using those elements.

 

We can quickly realize how software that includes these elements can use both concept and none of them is wrong.

 

so, why this is such a popular topic?

 

Let’s analyze the problem in a few examples

 

Our client is the owner of a dealer in downtown, his main business is to sell cars and he does two types of deals,

 

Either the cars are sold outright or he leases them to the customers.

 

 

This diagram is called UML if this is the first time you came across it or you do not know the meaning of the symbols have a brief look at how to design your software using UML diagrams


The relationship between the classes here are quite straightforward, we should have an abstract Car class, and the two different type of concrete classes that inherits from it.

 

OnSaleCar and ForLeaseCar are both types of cars, hence the inheritance
(a car on sale is a car and a car for lease is a car)

 

So far so good.

 

This is the basics of OOP.

 

A year passed and the business is doing well, so well that the owner decided that he wants to start selling motorbikes too,

 

Since he still wants to sell and lease the vehicles the system should work the same way but now we need to implement this new product.

 

 

You can see that there is some problem here, we have functions duplicated all over the place and now the Car and Motorbike classes have to mirror the attributes of the Vehicle class.

 

What happens if next year our client decides to add boats to his shop?

 

I would need to replicate 2 classes for each new type of vehicles every time.

 

It is easy to understand why Inheritance has let us down on this occasion, 

 

To be clear the software works, but with this huge cost in maintenance.

 

We need to fix this,

 

The biggest problem shown in the previous UML is the duplication of methods in the concrete class.

 

A way to handle this issue is to remove all the logic from the inheritance and only calculate the cost of the deal in the abstract class Vehicle, we can do so using conditional statements such an if or even better switch case to manage future products.
 

abstract class Vehicle {
    ...
    public function cost (): float
    {
        switch ($this->dealType) {
           case 'sale':
              return 'on sale';
              Break;
          case 'lease':
              return 'for lease';
              break;
        }
    }
    ... 
}

 

 


As you can see from the last UML diagram this is already an improvement and now the system is a bit more easy to manage.

 

The issue that we are having right now is that the pseudocode in this implementation is using conditions rather the most favourable notion of polymorphism.

 

The problem is that we need to add this condition inside all the methods of the abstract class (duplication again) and if we add a new Boat class in the future we still need to update the switch statements in every methods within the abstract class.

 

Notice how I am thinking forward at a possibility that could happen a year from now or couldn't happen at all.

 

The goal is to create a scalable product with the least maintenance as possible.

 

To help in this we use the concept described in the next paragraph.

 

 

Composition

 

We have seen how since cars and motorbike are vehicles we can use Inheritance to create a system around them.

 

You have also just seen the limitation of inheritance on a long-term project and the high maintenance it would require to keep developing it.

 

We are now going to see how we can implement the concept of composition and improve the code in a reliable and scalable way.

 

What we need here is to define the different types of deal, we must put each of them into their own classes, and make the objects we obtain interchangeable from the vehicles objects.

 

 

 


Let’s review what’s happening here.

 

I have created the DealStrategy class in which I defined the cost of the deal and the dealType() method.

 

The cost() method requires an instance of Vehicle, which is used to generate the data.

 

We also have two different types of deal classes.

 

Currently, the vehicles work only with the DealStrategy class, the Vehicle object itself does not know which kind of deal it is in (which is good).

 

Another positive aspect of this refactoring is that we can add as many types of deals as we want without touching any vehicles class at all.

 

abstract class Vehicle {
    // other code here
    
    public function __construct (float $price, DealStrategy $deal) {
        $this->dealStrategy = $deal;
        // other code here
   }    

    public function dealType() :string {
         return $this->dealStrategy->dealType();
    }

    // other code here
}
abstract class DealStrategy {
    abstract public function dealType() : string
    // other code here
}

 

This allows us to do this:

$car= new Car(1000, new OnSaleStrategy());

 

We have just added the type of a deal on a car object at runtime, none of the vehicles classes cares about what happened, and we can add new vehicles and type of deals without warring about not scaling properly and frequent maintenance.

 

In jargon, this is called a “has-a” relationship.

 

The example you just saw is a design pattern called the Strategy pattern.

 

You can see another implementation from this post by James Sugrue.

 

The examples are in Java but I will cover several design patterns more in details soon (so SUBSCRIBE to the newsletter to get notified when they will be published).

 

 

 

Decoupling

 

I want to come back to the example we have just seen and analyze a bit more in details what was the problem that led us to divide the algorithm of the deal in several parts and having distinct classes for every deal.

 

The fundamental problem was that after some modification we were in the situation of needing to edit several methods if we had to update our code.

 

To be clear, if you had to update the switch statement in the cost method of the Vehicle you had to edit all the other methods that have this condition.

 

If you only forget one you would have broken the program, and to add to the disgrace the PHP engine would not have thrown any error.

 

Good luck debugging that!

 

The one described above is the concept of coupling.

 

As you can imagine, a well-structured system instead, would have a low level of coupling.

 

The act of losing the coupling of a class is called decoupling.

 

The goal is to have no knowledge of a part of the program in other parts of it.

 

As shown above, we can add, and change the type of the deal without updating a single line of code in any of the Vehicle-related classes.

 

You can find more info about decoupling in this post 

 

Code to an Interface

 

Another clear concept here is that we are not limiting our vehicle with a single type of deal, we can do so by injecting the DealStrategy into the constructor of the Vehicle class.

 

This allows us to pass any instances of deals to any type of vehicles.

 

What would happen if we had restricted the constructor and only requiring a LeaseStrategy instead? 

 

Since we are now requiring a concrete class rather than an interface ( in our case an abstract class) we now excluded our vehicle to any other possible type of deals.

 

I must say there are pro and cons to both ways of programming.

 

Coding to an interface makes the system more “elastic”, it adds polymorphism that is something that we want because we know that we will, sooner or later, need.

 

Coding to a concrete implementation instead is going to make our code more secure, we know for sure which type of object we are using all the time.

 

This high level of safety removes the polymorphism though, after a while, it will feel like coding with a pair of handcuff on.

 

Conclusion

 

We have been all over the place with this post.

 

From explaining what’s in the bad practice of using inheritances to describing its better version after we favour the composition method.

 

We have also seen an example that included the Strategy Design Pattern that let us leverage the power of polymorphism by decoupling our system and creating a relationship to interfaces rather than the different types of themself.

 

What can you do now?

 

Have a look at your code, look for elements in your classes that include elements that vary and encapsulate them.

 

You can also improve the quality of your web application adding some good practices such as sanitize users’ input or better handling error and exceptions


 

 
 
If you like this content and you are hungry for some more join the Facebook's community in which we share info and news just like this one!

Other posts that might interest you

Coding (Php 7.x) Oct 28, 2019

How to design your software using UML diagrams [with case study]

See details
Coding (Php 7.x) Nov 18, 2019

The PHP 7.4 guide (new features)

See details
Coding (Php 7.x) Nov 25, 2019

PHP 7.4 deprecated! (list of Deprecations)

See details
Get my free books' review to improve your skill now!
I don't want to improve