« Favour composition over inheritance( I ) | Inicio | Favour composition over inheritance ( and III ) »

Favour composition over inheritance ( II )

What happens if our class hierarchy is extremely deep?. Any change in any of the superclasses will mean that we’ll have to make a lot of changes in many different parts of our program.

Let’s go one step beyond. Suppose that there’s a method in HumanBeing like this one:

public function pay( ammount: Number ): Coin { return new Coin( ammount ); }

So any human being that has to make a payment, does it giving a coin of the given ammount. All developers and truckdrives extend HumanBeing, so they will implement this method. And any class that interacts with a developer or a truckdriver should accept coins as a payment method.

But, what happens if we need to change the payment method?. What happens if now, instead of giving coins, everybody should pay using a credit card?.

Any little change in the superclass interface will affect the subclasses, and can affect our program in many different places ( for instance, we cannot pay a bus ticket with a credit card, although we can pay for a meal at a restaurant using a credit card ).

But suppose we delegate the responsibility of payments in a different class, titled PaymentManager:

class PaymentManager { var actualFunds: Number; function PaymentManager( ){} public function init( initFund: Number ) { this.actualFunds = initFund; } public function pay( ammount: Number ): Coins { this.actualFunds-= ammount; return new Coin( ammount ); } }

Now, Developer and TruckDriver will look like this:

class Developer extends HumanBeing { var paymentManager: PaymentManager function Developer( ) { this.paymentManager = new PaymentManager( ); this.paymentManager.init( 100 ); } public funtion pay( ): Coin { return this.paymentManager.pay( 100 ); } }

If we need that the developer pays using a credit card, we’ll only have to change the Developer class implementation:

public function pay( ): CreditCard { return new CreditCard( this.paymentManager.pay( 100 ) ); }

Well, maybe this is not the best example possible, but I hope it helps to understand the concept.

But, also, if we’ve relied on inheritance only as a way of reusing code, we can find the opposite problem. We could need to change the interface of one of the subclasses, so we’ll have to change the interface of the superclass, which will introduce some changes in the rest of the subclasses. Let’s try again to show an example:

If the base class ( HumanBeing ) implements the following method:

public function getSocialSecurityNumber( ): String { return this.socialSecurityNumber; }

both Developer and TruckDriver will implement that method. But what if the developer’s employer asks him for his Social Security Number, but encrypted?. We’ll have to change the superclass, and that will change the TruckDriver class. You can argue that, well, that method could be overwritten ( in the Developer class ). But, then, there was not need to make that method part of the superclass. Why?. Because the implementation is not the same for all the subclasses, although the interface ( the name of the method ) is the same. So everything could have been implemented using a common interface.

Comentarios

Your mixing up a lot here, i think. If you want a human being to be able to pay either by coin or by credit card, you naturally can define this method (or two methods, doesn't matter) in the HumanBeing class. There is no need to define it in the subclasses. The basic principle should be, to only define general stuff into the superclass, and let the subclasses specialize on that. So it would be possible to implement a general pay function, which only contains the very basic procedures and then every subclass extends it, by writing its own pay function, but using super.pay() for reusing code.

Inheritance makes a lot of sense and by the way is not at all comparable to composition because we have two totally different concepts here. So of course there are situations, where i need the concept of composition but it doesn't mean it is better, because it depends on what you want to do.

>>> But, then, there was not need to make that method part of the superclass. Why?. Because the implementation is not the same for all the subclasses, although the interface ( the name of the method ) is the same. <<<

In this case, you could simply have the superclass methods be abstract (stubs), thus defining the interface for the subclasses.