I've been seeing some misuse of inheritance in Java lately, and wanted to share some thoughts on when to use inheritance, and when to use composition. As a general rule, I would say that I almost always try to use composition, except where it is particularly cumbersome. I'll try to explain some of the reasons for using composition instead of inheritance, along with some concrete examples.
1. Building a Better Map
Let's start with the example of building a case-insensitive map. We want a map where the key is always the lower-case toString() of the key. Let's try implementing this with inheritance.
public class NoCaseHashMap extends HashMap {
private String toKey(Object key) {
if (key == null) {
return null;
}
return key.toString().toLowerCase();
}
public Object get(Object key) {
return super.get(toKey(key));
}
public Object put(Object key, Object value) {
return super.put(toKey(key), value);
}
public boolean containsKey(Object key) {
return super.containsKey(toKey(key));
}
public Object remove(Object key) {
return super.remove(toKey(key));
}
}
Pretty easy, right? But what if we need a case-insensitive map where they keys are stored in alphabetical order? We'd probably want a TreeMap instead of HashMap. Do we create a new class, NoCaseTreeMap that extends TreeMap? That way lies madness. We'll end up having to copy-paste all the methods from our NoCaseHashMap. Let's try reimplementing it with composition and the decorator pattern.
(If you'd like a refresher on the wonderful decorator pattern, have a look here: http://www.javaworld.com/javaworld/jw-12-2001/jw-1214-designpatterns.html)
public class NoCaseMap implements Map {
private Map map;
public NoCaseMap(Map map) {
this.map = map;
}
private String toKey(Object key) {
if (key == null) {
return null;
}
return key.toString().toLowerCase();
}
public Object get(Object key) {
return map.get(toKey(key));
}
public Object put(Object key, Object value) {
return map.put(toKey(key), value);
}
public boolean containsKey(Object key) {
return map.containsKey(toKey(key));
}
public Object remove(Object key) {
return map.remove(toKey(key));
}
/* Note the need to implement putAll(map) with composition */
public void putAll(Map t) {
for (Iterator it = t.entrySet().iterator(); it.hasNext();) {
Map.Entry entry = (Map.Entry) it.next();
put(entry.getKey(), entry.getValue());
}
}
// --------------------------------------
// The rest of these are just the standard cruft of composition:
// --------------------------------------
public void clear() {
map.clear();
}
public boolean containsValue(Object value) {
return map.containsValue(value);
}
public Set entrySet() {
return map.entrySet();
}
public boolean isEmpty() {
return map.isEmpty();
}
public Set keySet() {
return map.keySet();
}
public int size() {
return map.size();
}
public Collection values() {
return map.values();
}
}
Obviously that's a lot more code, but it also gives us a lot more. We can have a new NoCaseMap(new TreeMap()) as we wanted, as well as a new NoCaseMap(new LinkedHashMap()) or a new NoCaseMap(new ConcurrentHashMap()), and so on and so forth. Eclipse helps us out with this a bit too. Those seven last methods can be implemented in a few keystrokes with Eclipse, with Source -> Generate Delegate Methods. You might notice this pattern is similar to InputStream and its family. It's also the way the JDK does it for things like Collections.unmodifiableMap. InputStream takes it a step further by having a FilterInputStream class that already implements all the methods as boilerplate methods calling the delegate, so if you want to decorate an InputStream, you can just extend FilterInputStream, and override the methods you care about. (Those of you who are particularly on the ball might comment that InputStream is a class, not an interface, but the pattern still applies.)
2. Teamwork is Great, but Sometimes You Can't Depend on Others
Another example, taken from recent work, would be a service that makes HTTP calls to a third party and parses the response. Since this service needs to make HTTP calls, let's extend our HttpClientImpl class.
public class WidgetServiceImpl extends HttpClientImpl implements WidgetService {
private static final String WIDGET_LIST_URL = "http://rest.widgets-inc.com/widgets";
private final WidgetListParser parser;
public WidgetServiceImpl(WidgetListParser parser) {
this.parser = parser;
}
public List<Widget> getWidgets() {
final String content = callHttp(WIDGET_LIST_URL);
return parser.parse(content);
}
}
This works fine, until you try to unit test it. How do you run your unit test if Widgets Unlimited's server is down? What if you're on the plane? Your WidgetServiceImpl is tightly coupled with HttpClientImpl and can't be tested without actually talking to this external server. We're depending on their server in order to test our code. Let's try again, but with composition.
public class WidgetServiceImpl implements WidgetService {
public static final String WIDGET_LIST_URL = "http://rest.widgets-inc.com/widgets";
private final WidgetListParser parser;
private final HttpClient httpClient;
public WidgetServiceImpl(HttpClient httpClient, WidgetListParser parser) {
this.httpClient = httpClient;
this.parser = parser;
}
public List<Widget> getWidgets() {
final String content = httpClient.callHttp(WIDGET_LIST_URL);
return parser.parse(content);
}
}
With an HttpClient being passed in now, we can pass in some dummy implementation that doesn't talk to a real server. It can do whatever we need in order to test our WidgetServerImpl.
(You may notice I've used HttpClient instead of HttpClientImpl. I like putting lots of interfaces on things, because it makes them more convenient to test with JMock.)
Note also that WidgetListParser is being passed in, not constructed within the WidgetServiceImpl. This also makes testing easier; if we mock/stub that, we can test WidgetServiceImpl without having to worry about what the content actually looks like. We'll test that separately when we test WidgetListParserImpl. Testing WidgetServiceImpl with JMock now is easy.
@RunWith(JMock.class)
public class WidgetServiceImplTest {
private Mockery mockery = new Mockery();
private HttpClient httpClient;
private WidgetListParser parser;
private WidgetService widgetService;
@Before
public void setUp() {
httpClient = mockery.mock(HttpClient.class);
parser = mockery.mock(WidgetListParser.class);
widgetService = new WidgetService(httpClient, parser);
}
@Test
public void shouldMakeHttpCallAndReturnWidgetList() {
final String content = "some dummy content";
final List<Widget> expectedWidgetList = new ArrayList<Widget>();
mockery.checking(new Expectations() {{
one(httpClient).callHttp(WidgetServiceImpl.WIDGET_LIST_URL);
will(returnValue(content));
one(parser).parse(content);
will(returnValue(expectedWidgetList));
}});
final List<Widget> actualWidgetList = widgetService.getWidgets();
Assert.assertSame(expectedWidgetList, actualWidgetList);
}
}
3. When to Use Inheritance and When to Use Composition
The traditional answer to the question is to use inheritance if there is an "is a" relationship between the classes and composition when there is a "has a" relationship. I don't think that's entirely sufficient though. I agree that if it's not an "is a" relationship, then it certainly shouldn't be done with inheritance. Is WidgetService an HttpClient? It uses an HttpClient to do it's job, but as far as consumers of the WidgetService are concerned, it could just as well talk to a database. But even if there is an "is a" relationship, there are plenty of times to use composition instead. Does the class you're extending have an interface? If you don't need to call protected methods of that class, consider composition instead. If a class ends with "Util" or "Helper", they should always be used via composition, not inheritance.
If any of this was new to you, you may also enjoy reading this:
http://www.tiedyedfreaks.org/eric/CompositionVsInheritance.html
Some of my examples were stolen liberally from that page.


