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.
Rules for good design of Class
Favour Immutability
Rules to make a class immutable
Advantage
Disadvantage
Best Practices
Some rules to follow
Some rules to follow when creating sub-classable classes
Prefer Interfaces to Abstract Classes
Advantages
Disadvantages
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.
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.
In the next post we will read about the best practices of converting C structures to Java.
No comments:
Post a Comment