Small
Functions should be small. You can fit about 150 characters in a line and 100 lines in a screen, so
- A line should not contain more than 150 characters
- A function should not be 100 lines long()
for example in Listing 3-3
public static String renderPageWithSetupsAndTeardowns(
PageData pageData, boolean inSuite) throws Exception{
if(isTest(pageData)){
includeSetupAndTeardownPages(pageData, isSuite);
}
return pageData.getHtml();
}
block and indenting
As we can see in the example
- blocks within
if
when
for
statements and so on should be one line long;
includeSetupAndTeardownPages(pageData, isSuite);
Functions should be large enough to hold nested structures;
- indent level of a function should not be greater than 1 or 2
Do one thing
Make sure your function only does one thing, which does steps that are only one level below the stated name of the function.
Purpose of it is to compose a larger concepot into the next level of abstraction.
How to know your function is doing more than one thing?
- Make sure you can extract a function from it with a name that isn’t merely restatement of the implementation.
One Level of Abstraction per Function
reading code from top to bottom - Stepdown Rule
- include setups and teardowns, we include setups, then testpage content, teardowns
- include setups, we include suite setup if this is a suite, the regular setup
- to include the suite setup serach parent hierarchy for …
- include setups, we include suite setup if this is a suite, the regular setup
make it like a tree.
Switch
Try to bury switch statements in a low-level class and never repeat them.
Bury them into an ABSTRACT FACTORY and use INHERITANCE and POLYMORPHISM, and the rest of the system can never read them.
//original
public Money calculatePay(Employee e) throws InvalidEmployeeType{
switch(e.type){
case COMMISSIONED:
return calComissionPay();
case HOURLY:
return calHourlyPay();
...
}
}
Codes above do more than one thing, and they will grow, and they violate SRP, OCP;
//ABSTRACT
public abstract class Employee(){
public abstract boolean isPayday();
public abstract Money calculatePay();
public abstract void deliveryPay(Money money);
}
public interface EmployeeFactory(){
public Employee makeEmployee(EmployeeRecord r) throws InvalidEmployeeType;
}
public class EmployeeFactoryImpl() implements EmployeeFactory{
public Employee makeEmployee(EmployeeRecord r) throws InvalidEmployeeType{
switch(r.type){
case COMMISSIONED:
return new CommissionedEmployee(r);
case HOURLY:
return new HourlyEmployee(r);
...
}
}
}
Descriptive Name
Do not be afraid to make the name long
- spend time choosing a name
- descriptive names will clarify the design
- be consistent in your names
Function Aruguments
More than three aruguments requires special justification
And output aruguments are harder to understand – cause us to do a double-take
Monadic Form
- First, make it clear to know it is a event
- If the function will change the input arugument, you are supposed to return it
StringBuffer transform(StringBuffer in)
is better thanvoid transform(StringBuffer in)
Flag Argument
Flag arguments are ugly, and it will make function do more than one thing, so if you need to distinguish if
statements, just get rid of the flag arguments and write two different functions.
Dyadic Functions and Triads
Convert them into monads or make the arguments in a natural order.
Argument Objects
if a function needs more than three arguments, they are supposed to be wrapped into a class.
makeCircle(int x, int y, int radius)
can be turned to makeCircle(Point center, int radius)
Argument List
When we want to pass a variable number of arguments into a function, we can make them equivalent to a single argument of type List
like the String.format()
function
Verbs and Keywords
Function can use verb/noun form to include arguments. Like writeField(name)
might be better than write(name)
. And assertExpectedEqualsActual(expect, actual)
might be better than assertEquals
Have no Side Effects
When you check a user password, only check it and DO NOT initialize it.(initialize is the side effect, and it might be coupling).
If someone call it inadvertantly, it will make the session data lost.
If you have to add initialization to the function, rename it! like checkPasswordAndInitSession()
(but it violates do one thing principle)
Output Arguments
when you want to output sth, it is not supposed to use it as a argument like
public void appendFooter(StringBuffer report)
, you can just change it as report.appendFooter()
Output arguments should be avoided
Command query Separation
A function either change the data state, or return some data information.
Avoid doing both at the same time like boolean set(String id, String name)
, which returns true when it successfully set the name and returns false to mean the id does not exist.
But it is not readable for readers when they see the codes
if(set("10", "bob")){
...
}
maybe it is better to seperate them
if(userIdExists("10")){
setUserName("10", "bob");
...
}
Prefer Exception to Error Codes
Throwing Exceptions might make your code cleaner compared to return error codes.
Extract the try/catch blocks
package your code and throw exception, then try/catch it at the most abstract level
public void delete(Page){
try{
deletePageAndAllReference(page);
}catch(Exception e){
log.info(e.getMessage());
}
}
public void deletePageAndAllReference(Page page){
page.delete();
register.deletePageReference(page.name);
configKeys.deleteKey(page.name.makeKey());
}
Error Handling is One Thing
there should be nothing after catch/finally
blocks
Error.java Dependency Magnet
Returning codes are basically defined in some enum or class
When the enum/class is changed, all other classes dependent on it will be recompiled and redeployed, so it will cause more burden.
Don’t repeat yourself
make the repeat statements become functions
Structured Programming
Edsger Dijkstra said
- every function/block within a function should have one entry and one exit
- one
return
in a function - never
break
orcontinue
in a loop - never ever use
goto
- one
It is hard, so occasional multiple return, break, continue statements do no harm if you keep your functions small, and sometimes they might be more expressive.
How to do it?
- first draft(even if it is clumsy and idsorganized)
- wordsmith, restructe and refine your draft
- mention
- indenting and nested loops
- long argument lists
- name
- duplicated code
- action
- split out functions
- change names
- eliminate duplications
- shrink and reorder method