Design Principles
1 All the Design Guidelines
8.9

Design Principles

1 All the Design Guidelines

By the end of the course, you will be expected to have a strong grasp of all of the design principles listed below, and be able to discuss and apply them in your own work. A few notes: First, many of them overlap with each other. For example, several are simply more specific reinforcements or examples of a broader principle. Second, some of them may contradict each other. With design, there is often no one right answer, no silver bullet, but a matter of balancing tradeoffs and meeting the needs of the specific situation you are designing for. Third, many of them are prescriptive: things you should always or never do; others are guidelines: things to aspire to but that cannot always be adhered to. Nearly all of these guidelines are linked to topics in Effective Java, Second Edition (and most of this advice is unchanged in the third edition, though the item numbers may shift around slightly), for further reading.

  1. Write Javadoc comments all public classes and methods. The comment for a class should be at least two sentences, and provide information not already clear from its definition. A person reading your comments should understand the purpose, details of inputs and outputs without actually reading its code! (Effective Java, item 44)

  2. Use interface types over concrete classes wherever possible. (Effective Java, items 18 and 52) Exception: immutable “value” objects (where all fields are final), or final classes with no interface.

  3. Fields must always be private. Exception: constants (i.e. static final fields). Methods and classes should be as private as possible. (Effective Java, items 13, 14 and 15)

  4. Classes should never have public methods that are not in the interface (aside from constructor) — and you should not “fix” this by blindly adding such methods to the interface.

  5. Favor composition over inheritance. (Effective Java, item 16)

  6. Catch and handle/report errors as early as possible. Use Java compiler checks, enums, and final as your first line of defense, and runtime checks second. (Effective Java, items 60 and 65)

  7. Use class types over Strings. (Effective Java, item 50)

  8. Check inputs. (Effective Java, items 38 and 40)

  9. Use exceptions only for exceptional situations – not for flow control. (Effective Java, item 57)

  10. Checked vs unchecked exceptions: A checked exception indicates a reasonable expectation that the program can recover. Unchecked exceptions indicate programmer error (may still be recoverable). (Effective Java, items 58 and 59)

  11. Don’t leave things in an inconsistent state for any substantive length of time. (Effective Java, items 1, 4, 6, 15 and 38)

  12. Beware of references, copies, and mutation. Make defensive copies. (Effective Java, item 39)

  13. Separate responsibilities: one class, one responsibility.

  14. Use class hierarchies and dynamic dispatch over tagged classes, complex if/switch statements. (Effective Java, item 20)

  15. Don’t duplicate code.

  16. Open for extension, closed for modification: make changes without modifying existing code; write code to support later changes without modification.

  17. Extensibility: design to make likely later changes easier.

  18. Write tests first, cover the range of situations, edge cases. Write code to be testable (avoid System.out); do not expose fields or add public methods just to allow for testing.

  19. Loose coupling over tight coupling (e.g. avoid hardcoding a reference to System.out). Write reusable components when possible.

  20. You can’t change an interface once it’s published.

  21. If you override equals(), override hashCode(), and vice-versa. (Effective Java, items 8 and 9)

  22. Reuse existing exceptions, classes, libraries, and designs. (Effective Java, item 47)