This shows you the differences between two versions of the page.
Both sides previous revision Previous revision | |||
java_best_practices [2012/08/23 09:52] hkoller [Testing] |
java_best_practices [2012/08/23 10:19] (current) hkoller [Exception Handling] |
||
---|---|---|---|
Line 1: | Line 1: | ||
+ | ====== Introduction ====== | ||
+ | This is a general list of guidelines and best practices collected from a variety of sources (books, interwebs, personal experience). All of these are guidelines should be taken with a grain of salt | ||
+ | |||
+ | some very helpful sources: | ||
+ | * Clean Code (Robert C. Martin) | ||
+ | * Effective Java (Joshua Bloch) | ||
+ | |||
+ | |||
+ | ====== General ====== | ||
+ | * Try to minimize the number of different languages (natural and artificial) in a source file | ||
+ | * Always follow the "Principle of the least surprise". Make things intuitive. | ||
+ | * Every class/method should implement the behaviour that another programmer could reasonably expect from the object's name (and nothing more). | ||
+ | * generally try to make the code as expressive as possible | ||
+ | * place code where a reader would expect it | ||
+ | * Do not suppress compiler warnings unless you have a very good reason | ||
+ | * document the reason | ||
+ | * suppress the warning as locally as possible | ||
+ | * Code Duplication is bad. DRY (Dont repeat yourself). | ||
+ | * although there are exceptions : Sometimes repeated code is preferable to creating dependencies between otherwise independent modules. | ||
+ | * Be **Consistent** | ||
+ | * do all similar things in the same way | ||
+ | * choose conventions and continue to follow them | ||
+ | * have only one name for one concept | ||
+ | * increase the expressiveness of calculations by using explanatory variables | ||
+ | * break calculations up by introducing intermediate variables with meaningful names | ||
+ | * prefer polymorphism to if/else or switch/case | ||
+ | * several similar switch/case statements are a good indicator that something is wrong with your code | ||
+ | * No magic numbers | ||
+ | * use well-named constants | ||
+ | * (does not apply only to numbers) | ||
+ | * Using float or double to represent currency is criminal | ||
+ | * use BigDecimal | ||
+ | * Ecapsulate Conditionals | ||
+ | * extract functions that explain the intent of the conditional | ||
+ | * Avoid negative conditionals | ||
+ | * they are much harder to understand | ||
+ | * Keep configurable data at high levels | ||
+ | * expose it as an argument to lower-level functions | ||
+ | * Prefer enums to constants | ||
+ | * Never write any code you dont need **right now** | ||
+ | * [[http://en.wikipedia.org/wiki/You_ain%27t_gonna_need_it|YAGNI]] | ||
+ | |||
+ | ====== Naming ====== | ||
+ | Names should | ||
+ | |||
+ | * be descriptive | ||
+ | * be unambiguos | ||
+ | * have appropriate length | ||
+ | * length of a name should be relative to the scope of the variable: the longer the scope the more precise the name should be | ||
+ | * as a rule: explanatory value outweighs length | ||
+ | * follow conventions (eg. of the domain) | ||
+ | * use standard nomenclature where possible | ||
+ | * be at the appropriate level of abstraction | ||
+ | * dont pick names that communicate implementation | ||
+ | * avoid encodings | ||
+ | * dont encode type information or visibility in variable names ( m_Name , s_Url) | ||
+ | * it is redundant, distracting and useless (the IDE does it better) | ||
+ | * dont't call an interface **I**Something (look at core java. List,Set,Map not IList,ISet,IMap) | ||
+ | |||
+ | ====== Comments ====== | ||
+ | Every time you write a comment you should feel **guilty**. This may sound radical but keep reading. If you write a comment you are basically admitting that you need to document a hidden concept or intention that you did not manage to **express within your code**. When you start writing a comment stop for a second and think hard if there exists a better way to express yourself within the programming language. This will not always be possible but here are a few points to consider: | ||
+ | * Comments need to be maintained separately. They grow stale or get separated from the code they were intended to document. They become inappropriate. | ||
+ | * Inappropriate comments can be distracting or even harmful | ||
+ | * They lead the programmer along the wrong way of thought | ||
+ | * Many inappropriate comments started out as being well intended, then the code evolved and the comment did not evolve with it | ||
+ | * Redundant comments are bad. Reiterating what your code already says is clutter at best. | ||
+ | * If a part of your program is so complicated that you feel the need to write a long comment then it is time to STOP. Refactor and simplify your code. | ||
+ | * Commented out code needs to be deleted. Always. (SVN will remember) | ||
+ | * Write comments **only** for things your code can not say for itself | ||
+ | * First try improving the naming of your variables, create adequately named helper methods or introduce clarifying variables | ||
+ | * Dont explain what code does (this MUST be obvious from the code, otherwise refactor until it is). Explain WHY you do it. | ||
+ | * If you write a comment make sure it is the best comment you can write | ||
+ | * use correct grammar and punctuation | ||
+ | * dont state the obvious | ||
+ | * be brief | ||
+ | |||
+ | |||
+ | |||
+ | ===== API Documentation (Javadoc) ===== | ||
+ | Public APIs should be well documented. But the above guidelines should be kept in mind. A comment like | ||
+ | |||
+ | <code java> | ||
+ | |||
+ | public interface Person { | ||
+ | /** | ||
+ | * Get the birthday of the associated person. | ||
+ | * | ||
+ | * @return the birthday of the Person as Date. | ||
+ | */ | ||
+ | public Date getBirthday(); | ||
+ | |||
+ | .... | ||
+ | } | ||
+ | </code> | ||
+ | |||
+ | adds NO INFORMATION to the API. It does however add unnecessary clutter. | ||
+ | |||
+ | What should be documented in a Javadoc API: | ||
+ | * things that are **not obvious** to the user of the API | ||
+ | * e.g. specific details of the contract | ||
+ | * requirements for the input variables | ||
+ | * (eg. "x must be >=0" ) | ||
+ | * the conditions under which checked and unchecked Exceptions are thrown | ||
+ | * eg. "@throws IllegalArgumentException if x<0" | ||
+ | * the state an object will be left in when an Exception is thrown | ||
+ | * side effects of a method (if any) | ||
+ | * eg. the method modifies input parameters | ||
+ | * eg. the method changes the state of the object (unless this is obvious) | ||
+ | * runtime complexity | ||
+ | * thread safety | ||
+ | * if a class is intended for inheritance (most classes really aren't) | ||
+ | |||
+ | ===== Example ===== | ||
+ | A trivial example: Instead of doing | ||
+ | |||
+ | <code java> | ||
+ | ... | ||
+ | if (record.getDate().before(MIN_DATE) ) { // remove if too old | ||
+ | remove(record); | ||
+ | } | ||
+ | </code> | ||
+ | |||
+ | do | ||
+ | |||
+ | <code java> | ||
+ | |||
+ | ... | ||
+ | removeIfTooOld(record); | ||
+ | .... | ||
+ | |||
+ | private void removeIfTooOld(Record r) { | ||
+ | if (r.getDate().before(MIN_DATE) { | ||
+ | remove(r); | ||
+ | } | ||
+ | } | ||
+ | </code> | ||
+ | |||
+ | your intention is now encoded in the language itself, a comment is no longer required. | ||
+ | |||
+ | ====== Classes / Interfaces ====== | ||
+ | * Helpers/utility classes are candidates for inner classes | ||
+ | * Classes that are not utilities of some other class should not be scoped inside another class | ||
+ | * Keep all Interfaces as small as possible | ||
+ | * hide as much implementation detail as possible (without being overly constraining) | ||
+ | * make stuff private unless you have a good reason to do otherwise | ||
+ | * don't create lots of package/protected methods and variables (they are implementation details and other classes WILL try to depend on them) | ||
+ | * Possible Exception: Unit Tests (hotly debated) | ||
+ | * Base classes should know nothing about their derivatives | ||
+ | * otherwise the base class depends on the subclass (which is generally not what you want) | ||
+ | * Avoid artificial coupling | ||
+ | * things that don't depend upon each other should not be coupled | ||
+ | * take the time to figure out where things are supposed to be declared | ||
+ | * Avoid [[http://sourcemaking.com/refactoring/feature-envy|Feature Envy]] | ||
+ | * Avoid transitive navigation | ||
+ | * myObject.getB().getC().doSomething() makes it difficult to change the design. The architecture becomes rigid. | ||
+ | * it would be better if myObject offered the service we need: myObject.doSomething() | ||
+ | * Objects should primarily interact via [[http://en.wikipedia.org/wiki/Message_passing | message passing ]] | ||
+ | * most of your code should ask objects to DO something | ||
+ | * if you spend a lot of time asking objects about their internal state you are probably doing something wrong | ||
+ | |||
+ | ===== Inheritance ===== | ||
+ | * Design classes for inheritance or explicitly forbid inheritance (private constructor, final class) | ||
+ | * If you override a method ALWAYS declare @Override | ||
+ | * Classes designed for inhertiance need to document their implementation (to a certain extent) | ||
+ | * Protected methods are effectively part of the public interface | ||
+ | * Subclasses can depend on them and will break if they change in the future | ||
+ | * Classes designed for inheritance should never call their own public/protected methods | ||
+ | * a Subclass might override them and break the class invariant of the base class | ||
+ | * ESPECIALLY not in constructors (the subclass which overrides the method will not be initialized yet --> chaos) | ||
+ | * Composition is often preferable to inheritance | ||
+ | * Interface inheritance is fine though | ||
+ | |||
+ | |||
+ | ====== Methods ====== | ||
+ | * methods should never contain anything that comes as a surprise to the reader | ||
+ | * a method should do exactly one thing | ||
+ | * each method should be simple and as short as possible | ||
+ | * avoid control flows that are hard to follow | ||
+ | * avoid deeply nested constructs | ||
+ | * Pick clear, descriptive names | ||
+ | * the method name should give the reader a good idea of what the method does | ||
+ | * if you have to look at the implementation to find out what the method does, then pick a better name | ||
+ | * if you are tempted to put an AND in a method name, it may be a good idea to split the method | ||
+ | * Never return null when you could return an empty List (Set, Map, etc.) | ||
+ | * this avoids many useless if(x!=null) checks (and NullPointerExceptions) | ||
+ | * ESPECIALLY don't do this for performance reasons (use Collections.emptyList() ) | ||
+ | * class methods that are used together should be close together | ||
+ | * order methods in a class so they can be read "top down: first a public method, then all private methods used by the public method | ||
+ | * try to stay at the same level of abstraction | ||
+ | * separate higher-level concepts from detailed concepts | ||
+ | * within a method don't mix levels of abstraction. It is deeply confusing. | ||
+ | * This also applies to Exceptions: a high-level method should NOT need to deal with low-level Exceptions | ||
+ | * prefer nonstatic methods | ||
+ | * only make functions static if they dont need an instance (duh) and you are sure you will never want this method to behave polymorphically | ||
+ | * when in doubt make the function nonstatic | ||
+ | |||
+ | ===== Arguments ===== | ||
+ | * Functions should have a small number of arguments (No argument is best, 3 should be max) | ||
+ | * Output arguments are counterintuitive. Readers expect arguments to be INPUTS. Dont confuse them. | ||
+ | * Boolean arguments are bad | ||
+ | * especially in public APIs | ||
+ | * a boolean argument is good indicator that a method does more than one thing and should be split | ||
+ | * a call like <code java> processUsers(true); </code> is not very helpful. Better: <code> processAdminUsers(); </code> | ||
+ | * Methods that are never called are dead code and should be discarded | ||
+ | * unless they are part of a public API | ||
+ | |||
+ | ====== Testing ====== | ||
+ | * Only test things that are part of the class contract | ||
+ | * eg. on a method that returns Collection<Integer> don't rely on the order of the elements during testing | ||
+ | * otherwise you hardcode a condition that is not part of the class contract | ||
+ | * Test everything that could possibly break | ||
+ | * dont skip trivial tests | ||
+ | * Tests also have documentary value | ||
+ | * at the very least they document how you **think** a class should behave | ||
+ | * Tests should be precise | ||
+ | * Always test boundary conditions | ||
+ | * Test exhaustively near bugs | ||
+ | * maybe a second/dependent bug is hiding nearby | ||
+ | * Tests should be fast | ||
+ | * otherwise people will not run them | ||
+ | * choose minimal examples that are sufficient to prove that a method behaves correctly | ||
+ | * Don't write several tests for the same thing | ||
+ | * Don't test more than one thing per @Test | ||
+ | * some people advocate only putting a single assert statement in every test method | ||
+ | * Make tests self-documenting | ||
+ | * descriptive names | ||
+ | * clear assert error messages | ||
+ | * develop a "testing language" on top of junit | ||
+ | * Don't accept failing unittests. ever. | ||
+ | * if a test fails fix it | ||
+ | * if a test is obsolete delete it | ||
+ | * if a test is not ready @Ignore it until it is ready | ||
+ | * If you ignore a unittest, document why you ignore it: @Ignore("Test case not ready") | ||
+ | * Keep the unittests healthy | ||
+ | * if you can't rely on the tests they become useless | ||
+ | |||
+ | ====== Exception Handling ====== | ||
+ | Here are a few guidelines | ||
+ | |||
+ | * Use Exceptions only for exceptional cases | ||
+ | * not for normal flow-control (eg. to terminate a loop) | ||
+ | * Before throwing an Exception think how the user of the API can/will react to it | ||
+ | * Use **checked Exceptions** when | ||
+ | * the user of your API can realistically be expected to recover (or at least do something useful after the exception has happened) | ||
+ | * you want the user of the API to be aware of an exceptional situation (a checked Exception forces him to handle it) | ||
+ | * Use **unchecked Exceptions** | ||
+ | * to indicate programming errors (IllegalArgumentException, IllegalStateException, NullPointerException) | ||
+ | * to abort in unrecoverable situations | ||
+ | * Provide meaningful error messages with your Exceptions | ||
+ | * describe exactly what has happened and why it was bad. This makes your code far easier to debug. | ||
+ | * Use Exceptions at the right level of abstraction | ||
+ | * don't force high-level methods to deal with low-level Exceptions | ||
+ | * employ Exception translation and Exception chaining | ||
+ | * Don't declare to throw multiple Exceptions unless they actually make a difference to the caller | ||
+ | * if they all indicate the same thing, translate the Exception into a more appropriate one | ||
+ | * Try to re-use existing Exceptions | ||
+ | * Document the conditions under which an Exception is thrown | ||
+ | * for checked and unchecked Exceptions ( "@throws IllegalStateException if initialize() has not been called" ) | ||
+ | * also document if an object is left in an illegal state after an Exception | ||
+ | * Do not "swallow Exceptions" | ||
+ | * especially not when you catch something as general as "Exception" | ||
+ | * the least you can do is to write a log message | ||
+ | * If there is a really good reason for an empty catch block | ||
+ | * catch the most specific Exception you can | ||
+ | * document why the catch block is empty | ||
+ | |||
+ | |||
+ | |||
+ | |||
+ | |||
+ | |||
+ | |||