It all come down to the word "change". Change is the key to decided which one should be inherited and which one should be composite.
The base class Duck has-a name, and swim. So does the MallardDuck which inherited from Duck. It is-a Duck after all. MallardDuck also has-a behavior fly and quack. But these two behavior are not inherited from Duck thou. Both are composite part of MallardDuck.
What make swim, different from fly? Can we consider swim as a behavior, and implement it in the same way as fly?
The answer is in the word "change". The swim does not change. All subclass has the same swim method. While, fly and quack are not the same in all subclass. Of course, nothing prevent us from implementing swim in the same way as we did with fly. But why do we need to do more complicate code if we don't need too. Inherited would work great for swim.
Can we just override and make change in the subclass as needed?
Yes, we can. However, if the change are the same in two different subclass, then we code duplication! Consider, both MallardDuck and RedheadDuck can fly with wing. That means we would have to make the same change to MallardDuck and RedheadDuck. By implement the part of the duck that can be changed into module, we can pick and choose what behavior we want. You can see in the part 3, it is make much more sense, easily to understand and maintain.
Object vs Module, which one should I use?
Obviously, if you want ability to change the behavior at run time, you would have to use object approach. But that is not the only advantage object approach has over module. The object give you much control over namespace and help you avoid name collision.
Consider the module implementation, when we include the module, all the methods are defined in the same name space. Which means it's possible we can get name collision. Specially, if the module are large, and very complicated, say 10 or may be 20 methods defined. It's possible two modules have the same method name but doing different thing. For example, if we happen to include both Behavior::FlyWithWing and Behavior::NotFly, one of the fly method will get overridden.
On the other hand, if we implement using objects, it's name collision won't happen, because all the methods would be contained in the object itself.
For the problem that is not so complex, the module would be a good choice, because it's easier to understand, and manage. Beside, with good naming convention, the name collision can be avoid easily.
Knowing when to use inheritance or composition is one of the most important skill to learn. What every way you choose to implement, separated part of the object that can be change, and use them to compost the new object is the key to handling change. If it's not change, just inherited it!
Always, start with the simple solution, but also know where are you heading help you make the right choice.
Thanks for reading this far. :)
Post a Comment