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.
Write Javadoc comments all
publicclasses 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)
Use interface types over concrete classes wherever possible. (Effective Java, items 18 and 52) Exception: immutable “value” objects (where all fields are
finalclasses with no interface.
Fields must always be
private. Exception: constants (i.e.
static finalfields). Methods and classes should be as private as possible. (Effective Java, items 13, 14 and 15)
Classes should never have
publicmethods that are not in the interface (aside from constructor) —
and you should not “fix” this by blindly adding such methods to the interface.
Favor composition over inheritance. (Effective Java, item 16)
Catch and handle/report errors as early as possible. Use Java compiler checks,
finalas your first line of defense, and runtime checks second. (Effective Java, items 60 and 65)
Use class types over
Strings. (Effective Java, item 50)
Check inputs. (Effective Java, items 38 and 40)
Use exceptions only for exceptional situations – not for flow control. (Effective Java, item 57)
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)
Don’t leave things in an inconsistent state for any substantive length of time. (Effective Java, items 1, 4, 6, 15 and 38)
Beware of references, copies, and mutation. Make defensive copies. (Effective Java, item 39)
Separate responsibilities: one class, one responsibility.
Use class hierarchies and dynamic dispatch over tagged classes, complex
switchstatements. (Effective Java, item 20)
Don’t duplicate code.
Open for extension, closed for modification: make changes without modifying existing code; write code to support later changes without modification.
Extensibility: design to make likely later changes easier.
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.
Loose coupling over tight coupling (e.g. avoid hardcoding a reference to
System.out). Write reusable components when possible.
You can’t change an interface once it’s published.
If you override
hashCode(), and vice-versa. (Effective Java, items 8 and 9)
Reuse existing exceptions, classes, libraries, and designs. (Effective Java, item 47)