C: Java programming guidelines

Bruce Eckel’s Thinking in Java Contents | Prev | Next

This
appendix contains suggestions to help guide you while performing low-level
program design, and also while writing code.

  1. 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.
    )

  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.
  3. 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.
  4. 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.)
  5. 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).
  6. Try
    to keep classes small and focused. Clues to suggest redesign of a class are:

    1)
    A complicated switch statement: consider using polymorphism

    2)
    A large number of methods that cover broadly different types of operations:
    consider using several classes

    3)
    A large number of member variables that concern broadly different
    characteristics: consider using several classes

  7. 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.
  8. 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.
  9. If
    you must do something ugly, at least localize the ugliness inside a class.
  10. 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).
  11. Use
    comments liberally, and use the
    javadoc
    comment-documentation
    syntax to produce your program documentation.
  12. 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.
  13. 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.
  14. 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.)
  15. 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.
  16. 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.
  17. 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.
  18. 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.
  19. 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.)
  20. Objects
    should not simply hold some data; they should also have well-defined behaviors.
  21. 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.
  22. 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.
  23. 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.
  24. 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.
  25. 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.
  26. 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.
  27. 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.
  28. 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.
  29. 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.
  30. 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.
  31. You
    can find other programming guidelines on the Web. A good set of links can be
    found at

    http://www.ulb.ac.be/esp/ip-Links/Java/joodcs/mm-WebBiblio.html

More by Author

Must Read