Bruce Eckel’s Thinking in Java | Contents | Prev | Next |
appendix contains suggestions to help guide you while performing low-level
program design, and also while writing code.
- Capitalize
the first letter of class names. The first letter of fields, methods, and
objects (handles) should be lowercase. All identifiers should run their words
together, and capitalize the first letter of all intermediate words. For example:ThisIsAClassName
thisIsAMethodOrFieldName
Capitalize
all
the letters of
static
final
primitive identifiers that have constant initializers in their definitions.
This indicates they are compile-time constants.Packages
are a special case: they are all lowercase letters, even for intermediate
words. The domain extension (com, org, net, edu, etc.) should also be
lowercase. (This was a change between Java 1.1
and Java 1.2.) - When
creating a class for general-purpose use, follow a “canonical form”
and include definitions for
equals( ),
hashCode( ),
toString( ),
clone( )
(implement
Cloneable),
and implement
Serializable. - For
each class you create, consider including a
main( )
that contains code to test that class. You don’t need to remove the test
code to use the class in a project, and if you make any changes you can easily
re-run the tests. This code also provides examples of how to use your class. - Methods
should be kept to brief, functional units that describe and implement a
discrete part of a class interface. Ideally, methods should be concise; if they
are long you might want to search for a way to break them up into several
shorter methods. This will also foster reuse within your class. (Sometimes
methods must be large, but they should still do just one thing.) - When
you design a class, think about the client programmer’s perspective (the
class should be fairly obvious to use) and the perspective of the person
maintaining the code (anticipate the kind of changes that will be made, to make
them easy). - Try
to keep classes small and focused. Clues to suggest redesign of a class are:1)
A complicated switch statement: consider using polymorphism2)
A large number of methods that cover broadly different types of operations:
consider using several classes3)
A large number of member variables that concern broadly different
characteristics: consider using several classes - Keep
things as “
private
as possible.” Once you publicize an aspect of your library (a method, a
class, a field), you can never take it out. If you do, you’ll wreck
somebody’s existing code, forcing them to rewrite and redesign. If you
publicize only what you must, you can change everything else with impunity, and
since designs tend to evolve this is an important freedom. Privacy is
especially important when dealing with multithreading – only
private
fields can be protected against un-
synchronized
use. - Watch
out for “giant object syndrome.” This is often an affliction of
procedural programmers who are new to OOP and who end up writing a procedural
program and sticking it inside one or two giant objects. With the exception of
application frameworks, objects represent concepts in your application, not the
application. - If
you must do something ugly, at least localize the ugliness inside a class. - Anytime
you notice classes that appear to have high coupling with each other, consider
the coding and maintenance improvements you might get by using inner classes
(see “
Improving
the code with an inner class
”
on page
759). - Use
comments liberally, and use the
javadoc
comment-documentation
syntax to produce your program documentation. - Avoid
using “magic numbers,” which are numbers hard-wired into code.
These are a nightmare if you need to change them, since you never know if
“100” means “the array size” or “something else
entirely.” Instead, create a constant with a descriptive name and use the
constant identifier throughout your program. This makes the program easier to
understand and much easier to maintain. - In
terms of constructors and exceptions, you’ll generally want to re-throw
any exceptions that you catch while in a constructor if it causes failure of
the creation of that object, so the caller doesn’t continue blindly,
thinking that the object was created correctly. - If
your class requires any cleanup when the client programmer is finished with the
object, place the cleanup code in a single, well- defined method with a name
like
cleanup( )
that clearly suggests its purpose. In addition, place a
boolean
flag in the class to indicate whether the object has been cleaned up. In the
finalize( )
method for the class, check to make sure that the object has been cleaned up
and throw a class derived from
RuntimeException
if it hasn’t, to indicate a programming error. Before relying on such a
scheme, ensure that
finalize( )
works
on your system. (You might need to call
System.runFinalizersOnExit(true)
to ensure this behavior.) - If
an object must be cleaned up (other than by garbage collection) within a
particular scope, use the following approach: Initialize the object and, if
successful, immediately enter a
try
block with a
finally
clause that performs the cleanup. - When
overriding
finalize( )
during inheritance, remember to call
super.finalize( )
(this is not necessary if
Object
is your immediate superclass). You should call
super.finalize( )
as the
final
act of your overridden
finalize( )
rather than the first, to ensure that base-class components are still valid if
you need them. - When
you are creating a fixed-size collection of objects, transfer them to an array
(especially if you’re returning this collection from a method). This way
you get the benefit of the array’s compile-time type checking, and the
recipient of the array might not need to cast the objects in the array in order
to use them. - Choose
interfaces
over
abstract
classes. If you know something is going to be a base class, your first choice
should be to make it an
interface,
and only if you’re forced to have method definitions or member variables
should you change it to an
abstract
class. An
interface
talks
about what the client wants to do, while a class tends to focus on (or allow)
implementation details. - Inside
constructors, do only what is necessary to set the object into the proper
state. Actively avoid calling other methods (except for
final
methods) since those methods can be overridden by someone else to produce
unexpected results during construction. (See Chapter 7 for details.) - Objects
should not simply hold some data; they should also have well-defined behaviors. - Choose
composition first when creating new classes from existing classes. You should
only used inheritance if it is required by your design. If you use inheritance
where composition will work, your designs will become needlessly complicated. - Use
inheritance and method overriding to express differences in behavior, and
fields to express variations in state. An extreme example of what not to do is
inheriting different classes to represent colors instead of using a
“color” field. - To
avoid a highly frustrating experience, make sure that there’s only one
class of each name anywhere in your classpath. Otherwise, the compiler can find
the identically-named other class first, and report error messages that make no
sense. If you suspect that you are having a classpath problem, try looking for
.class
files with the same names at each of the starting points in your classpath. - When
using the event “adapters” in the Java 1.1
AWT, there’s a particularly easy pitfall you can encounter. If you
override one of the adapter methods and you don’t quite get the spelling
right, you’ll end up adding a new method rather than overriding an
existing method. However, this is perfectly legal, so you won’t get any
error message from the compiler or run-time system – your code simply
won’t work correctly. - Use
design patterns to eliminate “naked functionality.” That is, if
only one object of your class should be created, don’t bolt ahead to the
application and write a comment “Make only one of these.” Wrap it
in a singleton. If you have a lot of messy code in your main program that
creates your objects, look for a creational pattern like a factory method in
which you can encapsulate that creation. Eliminating “naked
functionality” will not only make your code much easier to understand and
maintain, it will also make it more bulletproof against the well-intentioned
maintainers that come after you. - Watch
out for “analysis paralysis.” Remember that you must usually move
forward in a project before you know everything, and that often the best and
fastest way to learn about some of your unknown factors is to go to the next
step rather than trying to figure it out in your head. - Watch
out for premature optimization. First make it work, then make it fast –
but only if you must, and only if it’s proven that there is a performance
bottleneck in a particular section of your code. Unless you have used a
profiler to discover a bottleneck, you will probably be wasting your time. The
hidden cost of performance tweaks is that your code becomes less understandable
and maintainable. - Remember
that code is read much more than it is written. Clean designs make for
easy-to-understand programs, but comments, detailed explanations, and examples
are invaluable. They will help both you and everyone who comes after you. If
nothing else, the frustration of trying to ferret out useful information from
the online Java documentation should convince you. - When
you think you’ve got a good analysis, design, or implementation, do a
walkthrough. Bring someone in from outside your group – this
doesn’t have to be a consultant, but can be someone from another group
within your company. Reviewing your work with a pair of fresh eyes can reveal
problems at a stage where it’s much easier to fix them and more than pays
for the time and money “lost” to the walkthrough process. - Elegance
always pays off. In the short term it might seem like it takes much longer to
come up with a truly graceful solution to a problem, but when it works the
first time and easily adapts to new situations instead of requiring hours,
days, or months of struggle, you’ll see the rewards (even if no one can
measure them). And there’s nothing that matches the feeling that comes
from knowing you’ve got an amazing design. Resist the urge to hurry; it
will only slow you down. - You
can find other programming guidelines on the Web. A good set of links can be
found athttp://www.ulb.ac.be/esp/ip-Links/Java/joodcs/mm-WebBiblio.html