This is the fifth in the series of posts based on the book Effective Java by Joshua Bloch. The previous post was about best practices in converting C structures to Java.
This post gives tips and best practices on how to write good methods in the classes.
This post gives tips and best practices on how to write good methods in the classes.
Check Parameters for Validity
Methods that expect parameters expect the parameters to adhere to
certain rules. In all public methods one should validate the parameters to
ensure that they adhere to the expected rules and if not they should throw the right
exception. It is also important that the constraints on the parameters be well
documented.
For private methods we should ensure that we pass only the right
attributes and instead of checking and throwing exceptions use Assertions.
It is very important that the parameters to the constructors are
validated correctly.
The only exception under which one should not validate the parameters
is, when there is a huge overhead in the validation.
Make Defensive Copies when needed
Normally a class should not expect an external class to be able to
change the values of its attributes without its knowledge. But when a class
contains mutable objects it is easy to allow clients to change the attributes
without the class knowing it.
E.g. consider the following class:
public final class Period {
private final Date
start;
private final Date
end;
public Period(Date
start, Date end) {
if
(start.compareTo(end) > 0) {
throw new
IllegalArgumentException(start + “ after “ + end);
}
}
this.start =
start;
this.end = end;
}
}
public Date start() {
return start;
}
}
public Date end() {
return end;
}
}
}
This class seems to be immutable as the only way to set the values of
the start and end dates are through the constructors. But the Date class itself
is a mutable class and there are two ways in which the values of the class can
be changed:
1.
A client which passes the start and end date to
the constructor can hold on the instance of the date and then at a later stage
can change the value of these dates using the setYear, setMonth or any of the
other methods.
2.
Similarly there is nothing to stop a client from
invoking something as Period.start.setYear(someYear).
To avoid these we need to make copies of such mutable classes. In the
constructor we can create new Date objects and store them instead of the passed
date objects.
public Period(Date
start, Date end) {
this.start = new
Date(start.time());
this.end= new
Date(end.time());
if (this.start.compareTo(this.end)
> 0) {
throw new
IllegalArgumentException(start + “ after “ + end);
}
}
}
Now even if the client changes the objects that it passed to the
constructor the values of the dates in the class will not change. Note that the
validation was done after the copy was done. This is to prevent the situation
where the value of the dates are changed after the validation and before the
creation of the new dates in some other thread.
Also the clone method is not used as Date can be subclassed and a
subclassed Date cannot be trusted.
Similarly we need to change the start and end methods to:
public Date start() {
return
start.clone();
}
}
public Date end() {
return
end.clone();
}
}
This will ensure that no client will be able to change the start and
end attributes of this class. Note that clone has been used here as we are sure
that we are using the java.util.Date and not a untrusted subclass of Date.
This is very important if we are writing a library for use by a large
audience over which we have no control.
Defensive copying can be avoided if we are working with a set of
trusted clients.
Design Method Signatures carefully
The following should be kept in mind when writing methods:
1.
Choose method names carefully: A method name
should convey to its user the exact operation it is performing. It should not
be necessary to further explain it in the method comments. What will be
required in the method comments will be to specify how it can be used and what
the parameter constraints are.
2.
Do not go overboard and provide too many
convenience methods: Too many makes understanding the class that much difficult
and that much more difficult to test. Provide convenience calls only when it is
expected that the method will be used frequently. If in doubt do not provide.
3.
Avoid long parameter list: It will be very
difficult to use a method which has a large number of parameters even if the
users have the benefit of an IDE which prompts for parameters. This is even
more problematic if there is sequence of parameters of the same type. Consider
creating a helper class or splitting the methods into multiple methods which
requires lesser number of parameters.
4.
When a
parameter being passed to a method has an interface define the parameter as the
interface rather than the concrete class that implements that interface. E.g.
if a list is expected as an input specify the parameter of type List rather
than specifying it of type ArrayList.
5.
Use Function Objects judiciously: Function
Objects have their place in software but when overused it can make
understandability of the program very difficult. It is not be shunned but it is
to be used only in situations where it makes sense and allows others to
understand it easily.
Function Objects are Objects which contain only Functions and no
attributes. These are typically used to handle callbacks in Java. E.g. a GUI
event handlers, or a class that implements Comparator interface and implements
only the compare method.
Use Overloading Judiciously
One needs to be careful when overloading functions and very clear. We
need to keep the following rule when we use overloading “selection among overloaded methods is static (i.e. determined at
compile time), while selection among overridden methods is dynamic”. So if
we overload methods then the method to be executed will be determined at
compile time and irrespective of the actual run time datatype the predetermined
method will be invoked.
As far as possible avoid having overloaded methods with the same number
of parameters. This makes overloading very safe. If one needs to write
overloaded methods with the same number of parameters then the parameter types
should be very different from each other to avoid confusion.
Return Zero-length Arrays and not nulls
If a method returns an Array and conditionally it returns a null then
client that invokes this method needs to check for nulls before proceeding. And
if some client were to forget checking for null we will end up with Null
Pointer Exception.
Instead if we return a zero length array as the output then no client
needs to check for the validity of the array and we can avoid Null Pointer
Exceptions.
Note that in most case the clients would have a loop which would be
looping through the array and doing some processing and this will be unaffected
if one returns a zero-length array.
Write doc comments for all exposed API elements
It is important to document all the public methods and attributes of a
class. This will help the users of the class use it in a much better way. It is
advisable to also document the protected and private methods if one wishes the
code to maintainable.
The method comments must have the following:
1.
What the method does. This should pretty much be
clear from the name of the method but in certain scenarios it will help if
there is some additional information provided. It should never mention how the
method functions.
2.
Do not have two methods or constructors with the
same summary description.
3.
It should mention all of the preconditions, i.e.
all the conditions that should be true before the method can be invoked.
4.
It should mention all of the postconditions,
i.e. all of the things that will be true after the method has been invoked.
5.
It should mention any side effects of invoking
the method
6.
The thread safety of the method should be
specified especially if it is not thread safe
7.
The purpose of the parameter along with its
constraints should be specified
8.
All the exceptions that the method can throw
should be documented and if possible with the condition under which it can be
thrown
9.
Do not have comments for getters and setters
unless something unusual is being done in the method.
In the next post we will read about best General Programming practices.
In the next post we will read about best General Programming practices.
No comments:
Post a Comment