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 to favor composition over inheritance
 
/img/blog/composition-over-inheritance-with-php-example.jpg

In this article, we'll explore a fundamental concept in object-oriented programming and why PHP developers need to consider using composition over inheritance.

 

Composition and inheritance are two fundamental concepts in object-oriented programming. 

Inheritance allows a class to inherit properties and behaviors from another class, forming a hierarchical relationship. 

 

On the other hand, composition is the idea of building complex objects by combining simpler ones. 

so, should I use composition over inheritance?

Choosing composition over inheritance offers numerous advantages, such as increased flexibility, reduced coupling, and better code maintainability. 

 

By favoring composition, developers can design more robust and extensible applications, leading to cleaner and more scalable codebases.

Let's dive into the world of composition and inheritance in PHP and discover the power of leveraging composition for building efficient and flexible applications.

 

The principle of Composition vs 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 favoring 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 vs 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 seem a bit too complicated you might want to brush up on the basics of PHP)

 

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

 

We can quickly realize how software that includes these elements can use both concepts 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 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 vehicle 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 as 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 than 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 method 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 possible.

 

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

 

 

Composition

 

We have seen how since cars and motorbikes 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 deals, we must put each of them into their 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 vehicle 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 deal on a car object at runtime, none of the vehicle classes cares about what happened, and we can add new vehicles and types 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 detail 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 detail what was the problem that led us to divide the algorithm of the deal into several parts and have 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 not know 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 vehicle.

 

What would happen if we had restricted the constructor and only required 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 from any other possible type of deals.

 

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

 

Coding to an interface makes the system more “elastic”, it adds polymorphism which 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 handcuffs on.

 

When inheritance is better than composition?

While composition is a valuable concept in object-oriented programming, there are situations where inheritance can be a better fit:

 

Code Reuse

Inheritance allows you to create a base class with common functionality that can be inherited by multiple subclasses. This promotes code reuse and reduces duplication.

 

Polymorphism

Inheritance enables polymorphism, which means objects of different classes can be treated as instances of their base class. This flexibility can be useful in various scenarios.

 

Hierarchical Relationships

Inheritance is suitable for modeling hierarchical relationships, where subclasses represent specialized versions of a more general base class.

 

Framework Requirements

Some PHP frameworks heavily rely on inheritance for defining model classes, controllers, and middleware. Using inheritance can align your code with the framework's conventions.

 

Interface Implementation

If multiple classes share a common interface, inheritance can simplify the process of implementing shared methods within the base class.

 

However, it's crucial to understand that while inheritance has its advantages, it can also lead to tightly coupled code and may limit the flexibility of your application's design.

Therefore, it's essential to carefully consider your project's needs and design goals before deciding whether inheritance is the best approach. In some cases, composition may offer a more flexible and maintainable solution.

 

Conclusion

In this post we have seen that composition vs inheritance is not a real battle.

in fact, both have reason to live in your codebase.

Yet, no one can deny the advantage of using composition in your codebase.

 

From explaining what’s in the bad practice of using inheritances to describing its better version after we favor 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 by adding some good practices such as sanitizing users’ input or better handling errors 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'll do myself