Favor Composition Over Inheritance in Java
Inheritance
Advantages:
Advantages :
Inheritance vs Composition Example
The example that i am going to discuss with you guys, comes from Effective java by Joshua
Bloch.Suppose we want a variant of HashSet that keeps track of the number of attempted insertions. So we subclass HashSet as follows:
References: Effective Java,OO Design Principles.
- Method of reuse in which new functionality is obtained by
extending the implementation of an existing object - The generalization class (the superclass) explicitly captures the
common attributes and methods - The specialization class (the subclass) extends the implementation
with additional attributes and methods
Advantages:
- New implementation is easy, since most of it is inherited
- Easy to modify or extend the implementation being reused
- Breaks encapsulation, since it exposes a subclass to implementation details
of its superclass - "White-box" reuse, since internal details of superclasses are often visible to
subclasses - Subclasses may have to be changed if the implementation of the superclass
changes - Implementations inherited from superclasses can not be changed at runtime
Advantages :
- Contained objects are accessed by the containing class solely through their
interfaces - "Black-box" reuse, since internal details of contained objects are not visible
- Good encapsulation
- Fewer implementation dependencies
- Each class is focused on just one task
- The composition can be defined dynamically at run-time through objects
acquiring references to other objects of the same type
- Resulting systems tend to have more objects
- Interfaces must be carefully defined in order to use many different objects
as composition blocks
Inheritance vs Composition Example
The example that i am going to discuss with you guys, comes from Effective java by Joshua
Bloch.Suppose we want a variant of HashSet that keeps track of the number of attempted insertions. So we subclass HashSet as follows:
package
aurangzeb.utk.com;
import
java.util.Arrays;
import
java.util.Collection;
import
java.util.HashSet;
public class InstrumentedHashSet
extends HashSet
{
// The number
of attempted element insertions
private int addCount = 0;
public
InstrumentedHashSet(Collection c) {
super(c);
}
public
InstrumentedHashSet(int initCap, float loadFactor) {
super(initCap,
loadFactor);
}
public
InstrumentedHashSet() {
// TODO
Auto-generated constructor stub
}
public boolean add(Object o) {
addCount++;
return super.add(o);
}
public boolean addAll(Collection
c) {
addCount += c.size();
return super.addAll(c);
}
public int getAddCount() {
return addCount;
}
Now lets test the above example and see the result which is 6,and the expected result should be 3,so we are getting the wrong output because of using inheritance.
why we are not getting the expected output the reason is obvious It’s because the internal implementation of addAll() in the HashSet superclass itself invokes the add() method. So first we add 3 to addCount in InstrumentedHashSet’s addAll(). Then we invoke HashSets addAll(). For each element, this addAll() invokes the add() method, which as overridden by InstrumentedHashSet adds one for each element. The result: each element is double counted.
There are several ways to fix the above problem
- There are several ways to fix this, but note the fragility of our subclass. Implementation details of our superclass affected the operation of our subclass.
- The best way to fix this is to use composition. Let’s write an
InstrumentedSet class that is composed of a Set object. Our
InstrumentedSet class will duplicate the Set interface, but all Set operations will actually be forwarded to the contained Set object. - InstrumentedSet is known as a wrapper class, since it wraps an
instance of a Set object. - This is an example of delegation through composition!
package
aurangzeb.utk.com;
import
java.util.Collection;
import
java.util.Iterator;
import java.util.Set;
public class InstrumentedSet
implements Set {
private final Set s;
private int addCount = 0;
public
InstrumentedSet(Set s) {this.s = s;}
public boolean add(Object o) {
addCount++;
return s.add(o);
}
public boolean addAll(Collection
c) {
addCount += c.size();
return s.addAll(c);
}
public int getAddCount() {return addCount;}
@Override
public void clear() {
// TODO
Auto-generated method stub
}
@Override
public boolean contains(Object
arg0) {
// TODO
Auto-generated method stub
return false;
}
@Override
public boolean containsAll(Collection
arg0) {
// TODO
Auto-generated method stub
return false;
}
@Override
public boolean isEmpty() {
// TODO
Auto-generated method stub
return false;
}
@Override
public Iterator
iterator() {
// TODO
Auto-generated method stub
return null;
}
@Override
public boolean remove(Object
arg0) {
// TODO
Auto-generated method stub
return false;
}
@Override
public boolean removeAll(Collection
arg0) {
// TODO
Auto-generated method stub
return false;
}
@Override
public boolean retainAll(Collection
arg0) {
// TODO
Auto-generated method stub
return false;
}
@Override
public int size() {
// TODO
Auto-generated method stub
return 0;
}
@Override
public Object[]
toArray() {
// TODO
Auto-generated method stub
return null;
}
@Override
public Object[]
toArray(Object[] arg0) {
// TODO
Auto-generated method stub
return null;
}
}
Note several things: This class is a Set It has one constructor whose argument is a Set The contained Set object can be an object of any class that implements the Set interface (and not just a HashSet) This class is very flexible and can wrap any preexisting Set object.
References: Effective Java,OO Design Principles.
