Tuesday, 1 February 2011

Comparing composition and inheritance 4


So how exactly do composition and inheritance compare? Here are several points of comparison:

  • It is easier to change the interface of a back-end class (composition) than a superclass (inheritance). As the previous example illustrated, a change to the interface of a back-end class necessitates a change to the front-end class implementation, but not necessarily the front-end interface. Code that depends only on the front-end interface still works, so long as the front-end interface remains the same. By contrast, a change to a superclass's interface can not only ripple down the inheritance hierarchy to subclasses, but can also ripple out to code that uses just the subclass's interface.
  • It is easier to change the interface of a front-end class (composition) than a subclass (inheritance). Just as superclasses can be fragile, subclasses can be rigid. You can't just change a subclass's interface without making sure the subclass's new interface is compatible with that of its supertypes. For example, you can't add to a subclass a method with the same signature but a different return type as a method inherited from a superclass. Composition, on the other hand, allows you to change the interface of a front-end class without affecting back-end classes.
  • Composition allows you to delay the creation of back-end objects until (and unless) they are needed, as well as changing the back-end objects dynamically throughout the lifetime of the front-end object. With inheritance, you get the image of the superclass 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.
  • It is 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 superclass interface, that code can work with a new subclass without change. This is not true of composition, unless you use composition with interfaces. Used together, composition and interfaces make a very powerful design tool. I'll talk about this approach in next month's Design Techniques article.
  • The explicit method-invocation forwarding (or delegation) approach of composition will often have a performance cost as compared to inheritance's single invocation of an inherited superclass method implementation. I say "often" here because the performance really depends on many factors, including how the JVM optimizes the program as it executes it.
  • With both composition and inheritance, changing the implementation (not the interface) of any class is easy. The ripple effect of implementation changes remain inside the same class.

Inheritance vs Composition

Choosing between composition and inheritance
So how do all these comparisons between composition and inheritance help you in your designs? Here are a few guidelines that reflect how I tend to select between composition and inheritance.
Make sure inheritance models the is-a relationship
My main guiding philosophy is that inheritance should be used only when a subclass is-a superclass. In the example above, an Apple likely is-a Fruit, so I would be inclined to use inheritance.
An important question to ask yourself when you think you have an is-a relationship is whether that is-a relationship will be constant throughout the lifetime of the application and, with luck, the lifecycle of the code. For example, you might think that an Employee is-a Person, when really Employee represents a role that a Person plays part of the time. What if the person becomes unemployed? What if the person is both an Employee and a Supervisor? Such impermanent is-a relationships should usually be modelled with composition.
Don't use inheritance just to get code reuse
If all you really want is to reuse code and there is no is-a relationship in sight, use composition.
Don't use inheritance just to get at polymorphism
If all you really want is polymorphism, but there is no natural is-a relationship, use composition with interfaces.

Implementing null-safe compareTo

// primarily by name, secondarily by value; null-safe; case-insensitive
public int compareTo(final Metadata other) {
int result = nullSafeStringComparator(this.name, other.name);
if (result != 0) {
return result;
}

return nullSafeStringComparator(this.value, other.value);
}

public static int nullSafeStringComparator(final String one, final String two) {
if (one == null ^ two == null) {
return (one == null) ? -1 : 1;
}

if (one == null && two == null) {
return 0;
}

return one.compareToIgnoreCase(two);
}

Monday, 31 January 2011

Composition Vs Inheritance 1

One of the fundamental activities of any software system design is establishing relationships between classes. Two fundamental ways to relate classes are inheritance and composition. Although the compiler and Java virtual machine (JVM) will do a lot of work for you when you use inheritance, you can also get at the functionality of inheritance when you use composition. This article will compare these two approaches to relating classes and will provide guidelines on their use.
First, some background on the meaning of inheritance and composition.
About inheritance
In this article, I'll be talking about single inheritance through class extension, as in:

class Fruit {

//...
}

class Apple extends Fruit {

//...
}
In this simple example, class Apple is related to class Fruit by inheritance, because Apple extends Fruit. In this example, Fruit is the superclass and Apple is the subclass.
I won't be talking about multiple inheritance of interfaces through interface extension. That topic I'll save for next month's Design Techniques article, which will be focused on designing with interfaces.
Here's a UML diagram showing the inheritance relationship between Apple and Fruit:



Inheritance relationship
Figure 1. The inheritance relationship
About composition
By composition, I simply mean using instance variables that are references to other objects. For example:

class Fruit {

//...
}

class Apple {

private Fruit fruit = new Fruit();
//...
}
In the example above, class Apple is related to class Fruit by composition, because Apple has an instance variable that holds a reference to a Fruit object. In this example, Apple is what I will call the front-end class and Fruit is what I will 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.
The UML diagram showing the composition relationship has a darkened diamond, as in:


Composition relationship
Figure 2. The composition relationship

Friday, 28 January 2011

Type erasure with generics

Type-safety checking of generic types is only performed at compile time. Type information is then removed and will not appear in the byte code. Consequently, a generic type like ArrayList<String> is translated to its ArrayList equivalent...

List<String> strings = new ArrayList<String>();
List<Integer> nums = new ArrayList<Integer>();
System.out.println(strings.getClass());
System.out.println(nums.getClass());

Both will print java.util.ArrayList. Type erasure means generic type information will not be available at runtime, and the following attempt to create an array of a generic type will not compile...
TreeSet<String>[] families = new TreeSet<String>[5];

The wildcard type is an exception to this :
List<?>[] info = new List<?>[5];

The ? stands for an unknown type that matches anything.

There is however, an upside to type erasure – there will be interoperability between generic type and raw types...

TreeSet<String> names = new TreeSet<String>();
TreeSet rawTree = names; //names is widened to raw type

Type-safety checking can be defeated, typically for backward compatibility. The following will compile, but with an “unchecked cast” warning...



This also works...

import java.lang.reflect.*;
...
TreeSet<String>[] people = (TreeSet<String>[])Array.newInstance(TreeSet.class,5);


But the way is now open for ClassCastException at runtime...
TreeSet rawTree = people[0];
oldTree.add(new Integer(1)); //compiles, but runtime error
Compile-time type-safety check warnings can be suppressed if needed...
@SuppressWarnings("unchecked")
public static void main (String[] args) {
...

}

Runtime operations such as instanceof are not permitted again, because of type erasure.

Similarly, a cast such as...
TreeSet<String> handles = (TreeSet<String>)name;
would produce an unchecked compiler warning because it is not something the run-time system can safely perform.

But why java uses Type erasures?
Type erasure is not a fault, rather it was clever step taken by java designers.
The reason why Java uses type erasure is to maintain backwards compatibility. Let's assume you wrote some code a few years back which used Java Lists, before generics was introduced in the language. Obviously your code had no mention of generics. Now, when engineers at Sun decided to introduce generics, they did not want to break code which people had already written.
One possible way was to ensure that client code which uses generics in the classes they invoke, never carries any information about generics in the compiled code. So the above class when compiled should not carry any information about generics. Because compiled client code never carries information about generics anyways, library implementers are free to add generics to their code without the worry of breaking anyone's old code.

Generic types are not covariant : Generics allow you to use only one type

All the Object-based collection types (known as raw types) in classic Java are now retrofitted as generic types and accept type parameters. Generic types allow you to express your intent as being restricted to a particular type when creating and using it...

TreeSet<String> names = new TreeSet<String>();
names.add("Fred");
names.add("Wilma");
names.add("Pebbles");

SortedSet<String> is called the parameterized type and String is the actual type argument. Type-safety checking will be performed at compile time...
names.add(new Integer(100)); //compilation error

The compiler will also reject the following...
TreeSet<Object> names = new TreeSet<String>();
TreeSet<Object> alias = (TreeSet<Object>)names;


This is because generic types are not covariant – a TreeSet of String references is not a TreeSet of Object references.
With generic types, retrieved elements do not require explicit conversion...

Wednesday, 22 December 2010

Getting java run time memory statistics

   public static void logJVMStatistics()
    {
        System.out.println("JVM Statistics -- Max : " + Runtime.getRuntime().maxMemory() + "; " +
                            "Free: " + Runtime.getRuntime().freeMemory() + "; " +
                            "Total: " + Runtime.getRuntime().totalMemory());
    }