Friday, November 09, 2012

Part III Effective Java - Classes and Interfaces

This is the third part of the series of posts based on the book Effective Java by Joshua Bloch. In the previous post we read about best practices in writing methods which are common to all classes.

This post is all about how to design classes and when and how to leverage the interfaces.

One of the suggestions is to use composition over hierarchy. This will definitely ruffle the feathers of many a hardcore Object Oriented followers. But after about 10 years and more in the Object Oriented world I tend to fully agree with the suggestion. It makes more sense to use composition over inheritance. Inheritance complicates more than it eases the problems. Composition helps keep the code base simple and uncomplicated.

Although the suggestions are basically for Java many of the suggestions applies also to the other Object Oriented Languages.


Minimize the accessibility of classes and members

In a module hide as much internal details as possible from the external world. Expose only that much as is required for the outside world to communicate with this module. This will ensure that that the module is isolated from the other modules interacting with it, making it easier to make changes to this without impacting the others.

Rules for good design of Class

1.       Make the classes and members as inaccessible as possible.
2.       All attributes other than constants (public static final) should be private to the class
3.       It is nearly always wrong to have public static final array field as the contents of the array are still modifiable. Ensure that objects referenced by public static final fields are immutable.

Favour Immutability

Whenever possible create immutable classes. This will ensure that they can be safely shared across multiple threads without worries about synchronization or race conditions.

Rules to make a class immutable

The make a class immutable the following rules need to be adhered to:
1.       Do not provide any method that can modify the object. Typically have only get methods and no set methods.
2.       Ensure that no method can be overridden. This will prevent the sub classes from breaking this rule.
3.       Make all fields final. This will ensure that one cannot change the value of the fields even from within the class.
4.       Make all fields private.
5.       Ensure exclusive access to any mutable components. If the class has to refer to a mutable class then ensure that this mutable class is never exposed outside of the class. If an instance of such an object needs to be returned from the class then make a copy and return the copy so that the original never changes.

Advantage

1.       Immutable classes need not ever be synchronized
2.       Their internals can be exposed without any problem

Disadvantage

1.       The only disadvantage of immutable class is that one needs to instantiate object everytime one needs the object with a different value. Creating too many objects can be a problem.

Best Practices

1.       Classes should be immutable unless there is a very good reason to make them mutable.
2.       If a class cannot be made immutable then the mutability should be limited.
3.       Constructors should create fully initialized objects with all of their invariants established

Favour composition over inheritance

A sub class depends on the implementation details of the super class. If the implementation of the super class changes the functionality of the sub-class is likely to be broken. Given this scenario it is good to go for composition rather than inheritance, except in cases where the class is meant to be sub-classed.

Some rules to follow

1.       Subclass only within a package so that a single set of developers has visibility into both the classes.
2.       Extend when the class is documented and specifically designed to be sub-classed.
3.       Add forwarding methods to expose the methods of the contained class with or without custom logic depending on requirement

Design and document for inheritance or prohibit it

If a class is expected to be a super class then document its methods properly otherwise prohibit inheritance by defining it as final.

Some rules to follow when creating sub-classable classes

1.       Constructors must not invoke overridable methods, directly or indirectly.
2.       Avoid implementing Serializable and Cloneable in a overridable class. If implemented neither clone or readObject may invoke an overridable method directly or indirectly.
3.       Make the readResolve or writeReplace methods protected instead of private so that they can be extended by the subclass if required.

Prefer Interfaces to Abstract Classes

Extending abstract class will have the same limitations as inheritance. As opposed to Abstract classes interfaces provide a lot of flexibility and abilities.

Advantages

1.       Existing classes can be easily retrofitted to implement a new interface
2.       Allow for nonhierarchical type framework
3.       Ideal for defining mixin. (A mixin is a type that allows a class to exhibit extra behavior in addition to its “primary type”. E.g. a class that implements Comparable states that it can be ordered).
4.       Interfaces enable safe, powerful functionality enhancements.
5.       It promotes loose coupling between modules.

Disadvantages

1.       The one disadvantage Interfaces have against the Abstract classes is that one can evolve abstract classes by adding concrete methods to the abstract class as and when required. This is not possible with interfaces as all the classes that implement the interface will now need to implement this method.

Use Interfaces only to define types

In short do not define an interfaces with no methods. Do not use interfaces which only define constants. It is confusing to have an Interface which does nothing.

Favour static member classes over nonstatic

A class defined within another class is a nested class. One can have one of the following types of nested  classes:
1.       static member classes
2.       nonstatic member classes
3.       anonymous classes
4.       local classes.
A static member class has access to all the members of the class in which it is declared including the private members.
Syntactically a non-static is same as the static class except for the absence of the static keyword. Every instance of a non-static class can refer to the instance of the enclosing class using the qualified this. One classic use-case of usage of non-static member class is Iterators in the Collection and Map classes.
If a member class does not require access to the enclosing instance then put the static modifier.
Anonymous classes are classes without name which are used for a particular call. Comparator is a good use-case where an anonymous class is created and passed as the comparator object to be used for sorting.
Local classes can be declared anywhere, where a local variable may be declared and they follow the same scope as a local variable. They have the characteristics of a non-static inner class and have access to the enclosing instance.

In the next post we will read about the best practices of converting C structures to Java.

No comments: