The Artima Developer Community
Sponsored Link

Designing with Interfaces
One Programmer's Struggle to Understand the Interface
by Bill Venners
First Published in JavaWorld, November 1998

<<  Page 4 of 6  >>

Advertisement

The 'burden' of implementation inheritance
Okay, my "more polymorphism" claim above is fairly straightforward and was probably obvious to many readers, but what do I mean by, "without the burden of multiple inheritance of implementation?" In particular, exactly how is multiple inheritance of implementation a burden?

As I see it, the burden of multiple inheritance of implementation is basically inflexibility. And this inflexibility maps directly to the inflexibility of inheritance as compared to composition.

By composition, I simply mean using instance variables that are references to other objects. For example, in the following code, class Apple is related to class Fruit by composition, because Apple has an instance variable that holds a reference to a Fruit object:

class Fruit {

    //...
}

class Apple {

    private Fruit fruit = new Fruit();
    //...
}

In this example, Apple is what I call the front-end class and Fruit is what I call the back-end class. In a composition relationship, the front-end class holds a reference in one of its instance variables to a back-end class.

In last month's edition of my Design Techniques column, I compared composition with inheritance. My conclusion was that composition -- at a potential cost in some performance efficiency -- usually yielded more flexible code. I identified the following flexibility advantages for composition:

The one flexibility advantage I identified for inheritance was:

In this last flexibilility comparison, however, inheritance is not as secure as it might seem given its polymorphism advantage. That last clause above, "unless you use composition with interfaces," is very important. Basically, thanks to interfaces, the composition relationship can also bask in the warm glow of polymorphism. Here's an example:

interface Peelable {

    int peel();
}

class Fruit {

    // Return int number of pieces of peel that
    // resulted from the peeling activity.
    public int peel() {

        System.out.println("Peeling is appealing.");
        return 1;
    }
}

class Apple implements Peelable {

    private Fruit fruit = new Fruit();

    public int peel() {
        return fruit.peel();
    }
}

class FoodProcessor {

    static void peelAnItem(Peelable item) {
        item.peel();
    }
}

class Example5 {

    public static void main(String[] args) {

        Apple apple = new Apple();
        FoodProcessor.peelAnItem(apple);
    }
}

Given the above set of classes, you could later define a class Banana like this:

class Banana implements Peelable {

    private Fruit fruit = new Fruit();

    public int peel() {
        return fruit.peel();
    }
}

Like Apple, class Banana has a composition relationship with Fruit. It reuses Fruit's implementation of peel() by explicit delegation: it invokes peel() on its own Fruit object. But a Banana object can still be passed to the peelAnItem() method of class FoodProcessor, because Banana implements the Peelable interface.

As this example illustrates, interfaces allow you to get the best of both worlds. You get the flexibility of composition and the flexibility of polymorphism in one design.

<<  Page 4 of 6  >>


Sponsored Links



Google
  Web Artima.com   
Copyright © 1996-2017 Artima, Inc. All Rights Reserved. - Privacy Policy - Terms of Use - Advertise with Us