Why favor composition over inheritance in Java OOP?
This is a very popular job interview question, and the correct answer depends on the problem you are trying to solve. You need to ask the right questions before deciding one over the other.
Q. How do you express an ‘is a’ relationship and a ‘has a’ relationship or explain inheritance and composition?
A. The ‘is a’ relationship is expressed with inheritance and ‘has a’ relationship is expressed with composition. Both inheritance and composition allow you to place sub-objects inside your new class. Two of the main techniques for code reuse are class inheritance and object composition.
Inheritance is uni-directional. For example House is a Building. But Building is not a House. Inheritance uses extends key word. Composition: is used when House has a Bathroom. It is incorrect to say House is a Bathroom. Composition simply means using instance variables that refer to other objects. The class House will have an instance variable, which refers to a Bathroom object.
Q. Which one to favor, composition or inheritance?
A. The guide is that inheritance should be only used when subclass ‘is a’ super class. Don’t use inheritance just to get code reuse. If there is no ‘is a’ relationship then use composition for code reuse.
Reason #1: Overuse of implementation inheritance (uses the “extends” key word) can break all the subclasses, if the super class is modified. Do not use inheritance just to get polymorphism. If there is no ‘is a’ relationship and all you want is polymorphism then use interface inheritance with composition, which gives you code reuse.
Reason #2: Composition is more flexible as it happens at run time whereas inheritance happens at compile-time.
Reason #3: Composition offers better testability than Inheritance. Composition is easier to test because inheritance tends to create very coupled classes that are more fragile (i.e. fragile parent class) and harder to test in isolation. The IoC containers like Spring, make testing even easier through injecting the composed objects via constructor or setter injection.
Q. Can you give an example of
the Java API that favors composition?
A. The Java IO classes that use composition to construct different combinations using the decorator design pattern at run time.
A. The Java IO classes that use composition to construct different combinations using the decorator design pattern at run time.
//construct a reader StringReader sr = new StringReader(“Some Text....”); //decorate the reader for performance BufferedReader br = new BufferedReader(sr); //decorate again to obtain line numbers LineNumberReader lnr = new LineNumberReader(br);
The GoF design patterns like strategy, decorator, and proxy favor composition for code reuse over inheritance.
Q. Can you a give an example where GoF design patterns use inheritance?
A. A typical example for using inheritance is in frameworks where the template method design pattern is used.
Another common pattern that would use inheritance is Composite pattern.
Q. What questions do you ask yourself to choose composition (i.e. has-a relationship) for code reuse over implementation inheritance (i.e. is-a relationship)?
A. Do my subclasses only change the implementation and not the meaning or internal intent of the base class? Is every object of type House really “is-an” object of type Building? Have I checked this for “Liskov Substitution Principle”
According to Liskov substitution principle (LSP), a Square is not a Rectangle provided they are mutable. Mathematically a square is a rectangle, but behaviorally a rectangle needs to have both length and width, whereas a square only needs a width.
Another typical example would be, an Account class having a method called calculateInterest(..). You can derive two subclasses named SavingsAccount and ChequeAccount that reuse the super class method. But you cannot have another class called a MortgageAccount to subclass the above Account class. This will break the Liskov substitution principle because the intent is different. The savings and cheque accounts calculate the interest due to the customer, but the mortgage or home loan accounts calculate the interest due to the bank.
Violation of LSP results in all kinds of mess like failing unit tests, unexpected or strange behavior, and violation of open closed principle (OCP) as you end up having if-else or switch statements to resolve the correct subclass. For example,
if(shape instanceof Square){ //.... } else if (shape instanceof Rectangle){ //... }
If you cannot truthfully answer yes to the above questions, then favor using “has-a” relationship (i.e. composition). Don't use “is-a” relationship for just convenience. If you try to force an “is-a” relationship, your code may become inflexible, post-conditions and invariants may become weaker or violated, your code may behave unexpectedly, and the API may become very confusing. LSP is the reason it is hard to create deep class hierarchies.
Always ask yourself, can this be modeled with a “has-a” relationship to make it more flexible?
For example, If you want to model a circus dog, will it be better to model it with “is a” relationship as in a CircusDog “is a” Dog or model it as a role that a dog plays? If you implement it with implementation inheritance, you will end up with sub classes like CircusDog, DomesticDog, GuideDog, SnifferDog, and StrayDog. In future, if the dogs are differentiated by locality like local, national, international, etc, you may have another level of hierarchy like LocalCircusDog, NationalCicusDog, InternationalCircusDog, etc extending the class CircusDog. So you may end up having 1 animal x 1 dog x 5 roles x 3 localities = 15 dog related classes. If you were to have similar differentiation for cats, you will end up having similar cat hierarchy like WildCat, DomesticCat, LocalWildCat, NationalWildCat, etc. This will make your classes strongly coupled.
Extracted from Core Java Career Essentials, which has more examples, code, and design topics.
Labels: design pattern, OO
0 Comments:
Post a Comment
Subscribe to Post Comments [Atom]
<< Home