« [software] Maxivista | Inicio | Favour composition over inheritance ( II ) »

Favour composition over inheritance( I )

Inheritance is a powerful tool. I can´t imagine my work without it. But it's not good to rely only on inheritance. Composition can be a better solution for many of our problems.

A few weeks ago, someone told me that the discusion about inheritance vs composition was like the discusion about PC vs Mac. But I don't think so. I believe that there are many points that suggest that we should favour composition.

Inheritance allows us to build a class using another class. Let’s try to see an example:

class HumanBeing { function HumanBeing( ) { } public function eat( ){} public function sleep( ){} public function speak( ){} } class Developer extends HumanBeing( ) { function Developer( ){} } class TruckDriver extends HumanBeing( ) { function TruckDriver( ){} }

Both the truck driver and the developer are human beings, some there will be some behavoiurs that are common to both of them ( for instance, eat, sleep, and speak ), and those behaviours are common to both of them just because they are human beings, and all the human beings eat, sleep, and speak. Well, so there we’ve found the two main concepts behind inheritance.

First, inhertiance allows to reuse code. When we need many different classes, that implement some common methods, we can implement that code into a base class, and make the other classes extend that base class. So, we can save us from having the same code in a lot of different places ( which can be a hell to maintain ).

Second, inheritance also allows us to group classes by the concepts they represent. In our example, both the truckdriver and the developer are human beigns, so it seems logical that both entities extend the HumanBeing class. In fact, that’s the most well-know rule to decide when a class must extend another one: the "is-a" relationship. The developer "is-a" human being, the truck driver "is-a" human being, so, both of them must extend the HumanBeing class.

There’s also another advantage of inheritance. And it is polymorphism. We can use any variable typed as HumanBeign to store a reference to any TruckDriver or Developer instances ( so, a variable typed as the base class – also called superclass - could store a reference to an instance of any of the subclasses ).

Composition, on the other hand, is a relationship that makes a class own another ( a class uses instance variables that are references to other classes ). Let’s see it with an example:

class Developer extends HumanBeing { var pet: Pet; function Developer( ) { this.pet = new Pet( ); } }

The developer has a pet. That’s the keyword to define a composition relationship: "has-a"

Until now, it seems clear when you should use inheritance and when you should use composition. So, why is this post titled "favour composition"?. Is there any doubt about when to use one or the other approach?

Yes, there are seriuous doubts about the benefits of inheritance under certain conditions. We’ll try to see some examples of inheritance relationships that are not the best of the available solutions.

First of all, we’ll go back to the first example. But now, we are going to build a class ( "Worker" ), that will implement all the behaviours of a working person, and that will extend the HumanBeing class ( a worker "is-a" human being ).

class Worker extends HumanBeing { function Worker( ){} public function doWork( ){} }

So now, the Developer and TruckDriver classes will be:

class Developer extends Worker { } class TruckDriver extends Worker { }

Now, both the developer and the truckdriver will inherit the methods of HumanBeing and the methods of Worker.

So, we have a superclass ( HumanBeing ). Worker extends HumanBeing( a worker "is-a" human being ), and both the developer and the truckdriver extend Worker ( a developer "is-a" worker, and a truckdriver is also a worker ). Right?

Hmmmmmmmmmm. Not at all. A developer is not always a worker. In fact, after work, some of them like to go out with their friends, or practice any sport ( for instance ). So a developer is not always a worker. And so, the Developer class should not extend the Worker class, because the behaviour of a developer will not always be the behaviour of a worker. In other words, the "is-a" rule should be "is-a, and only a, and always a".

In this case, for instance, the solution could be to make the functionallity of a worker be part of an interface, that both the Developer and the TrcukDriver class could implement, or even better, both the Developer and TruckDriver class could have an instance variable that could be an instance of the Worker class.

class Developer extends HumanBeing { var workerBehaviour: Worker = new Worker( ); public function doWork( ) { this.workerBehaviour.doWork( ); } }

This solution has another advantage. If we must change the interface of Worker, that change will only affect Developer and TruckDriver, because all the other classes that interact with Worker, will do it through Developer or TruckDriver. So, refactoring will be easier.

There will be more in the next post.

Comentarios

>>> A developer is not always a worker. <<<

Yes, that's why the Worker is also a HumanBeing. The HumanBeing class should implement all the necessary functions for doing fun things in its freetime. And automatically the Developer is also a HumanBeing and able to do lots of nice things in his freetime. I don't see any contradiction here.