Tuesday 15 March 2011

Java design Techniques – Using Interfaces Part – 2


This article will talk more about role of interfaces and its usage while choosing between multiple inheritance and composition in your design I personally believe multiple inheritance implementations is basically inflexible. 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 the front-end class and Fruit is the back-end class. In a composition relationship as a thumb rule, the front-end class always holds a reference in one of its instance variables to its back-end class.
Composition usually yields more flexible code. I identified the following flexibility advantages for composition:
  • It's easier to change classes involved in a composition relationship than it is to change classes involved in an inheritance relationship.
  • Composition allows you to delay the creation of back-end objects unless they're needed. It also allows you to change the back-end objects dynamically throughout the lifetime of the front-end object. However with inheritance this is not true, you get the image of the super class in your subclass object image as soon as the subclass is created, and it remains part of the subclass object throughout the lifetime of the subclass.
The one flexibility advantage I identified for inheritance was:
  • It's easier to add new subclasses (inheritance) than it is to add new front-end classes (composition), because inheritance comes with polymorphism. If you have a bit of code that relies only on a super class interface, that code can work with a new subclass without change. This isn't true for composition, unless you use composition with interfaces.
In this above flexibility 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. 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 Example {

    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 Fruitobject. 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.


No comments:

Post a Comment