When designing your domain/classes, this is one decision that always results in conflicts within teams. Whether to inherit from a class or to compose that class. This is fairly subjective but here are few pointers for discussion.
Let's take this code for example:
public class GiftCollection extends HashSet {
public Gift getRandomGift(){
List giftList = new ArrayList(this);
Collections.shuffle(giftList);
return giftList.get(0);
}
}
It is a collection of gifts that is represented as a set. The additional behavior from a set is that it can give you a random gift. It looks fairly straight-forward and would work in an even more straight-forward manner. Let's look at a usage of this.
public class GiftSelector {
GiftCollection giftCollection;
public GiftSelector(GiftCollection giftCollection) {
this.giftCollection = giftCollection;
}
public Gift selectRandomGift(){
return giftCollection.getRandomGift();
}
}
This is a basic usage of the GiftCollection
provided earlier. If one looks closely, though the two classes look very similar and doing similar things, there are basic differences between the two.
GiftCollection
inherits a HashSet
and extends to the behaviour by adding another method getRandomGift
to it. However, GiftSelector
decided to contain a GiftCollection
and delegate few calls to it.
I've always preferred the latter approach for the following reasons:
1. The classes (both superclass and subclass) are much more flexible to change.
In this case, if GiftCollection
decides to change the signature of the method getRandomGift()
, it would not have an effect on the callers of GiftSelector
, since it wraps the method call.
But any change to the Set
implementation would affect the API of the GiftCollection
class, without its knowledge. The users of GiftCollection
have to change their code, though there has been no apparent change to the class. This is not an ideal scenario.
2. Easier to test
It clearly shows that GiftSelector
is much easier to test than GiftCollection
. One can easily mock the dependency of the class and unit test the code. Whereas, in the case of inheritance, most mocking libraries do not provide a way to mock super classes. So it's difficult to test in isolation.
3. Polymorphism through Interfaces is more flexible
One of the reasons for inheritance has been to reduce code and take advantage of polymorphic behaviour. Interfaces provide a more flexible way to achieve the same result. Composition coupled with Interfaces present a better alternative to achieve polymorphism.
This is not to say that one should not use Inheritance.
When modeling your domain, Inheritance should be used in case of a direct ‘is-a’ relationship. It should not be used only to achieve code-reuse or polymorphism.
Prefer composition over inheritance, unless you have good reasons to not do so...
I would love to hear your thoughts on this.
CodeProject
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.