Clean code _v2003

46

description

Slides writing clean code. Inspired by Robert C. Martins book "Clean Code: A Handbook of Agile Software Craftsmanship".

Transcript of Clean code _v2003

Page 1: Clean code _v2003
Page 2: Clean code _v2003

Design Patterns in Visual Basic ???

Page 3: Clean code _v2003

Criteria used in defining clean codeThe code is mostly read and maintained, not

written, so:› SHOULD BE LITERATE (Reads like prose)

But also:› Elegant› Unit-testable› Has minimal dependencies› Clear and minimal API› One way rather than many to do things› Good error handling› Contains minimal duplication› Efficient

Page 4: Clean code _v2003

Meaningful names

Functions

Comments

Page 5: Clean code _v2003

Use intention revealing names

public List<int[]> getThem() {List<int[]> list1 = new ArrayList<int[]>();for (int[] x : theList)

if (x[0] == 4) list1.add(x);return list1;

}

Page 6: Clean code _v2003

› Problems : What kinds of things are in theList? Significance of the zeroth subscript? Significance of the value 4? The most significant problem is actually the

IMPLICITY of the code! (the code assumes we know the answers to questions like the ones above).

Page 7: Clean code _v2003

Refactored version (improved, yet not perfect):

public List<int[]> getFlaggedCells() {List<int[]> flaggedCells = new ArrayList<int[]>();for (int[] cell : gameBoard)

if (cell[STATUS_VALUE] == FLAGGED) flaggedCells.add(cell);

return flaggedCells;}

Page 8: Clean code _v2003

Make meaningful distinctions

public static void copyChars(char a1[], char a2[]){for (int i = 0; i < a1.length; i++) { a2[i] = a1[i];}

}

Page 9: Clean code _v2003

› Problems: a1, a2 have no meaningful names. A better version would replace the variable

name a1 and a2 with source, respectively destination.

Page 10: Clean code _v2003

Use pronounceable names

class DtaRcrd102 {private Date genymdhms;private Date modymdhms;private final String pszqint = "102";

};

Page 11: Clean code _v2003

Refactored version:

class Customer {private Date generationTimestamp;private Date modificationTimestamp;private final String recordId = "102";

};

Page 12: Clean code _v2003

Avoid encodings

› Hungarian notation Was considered important in the Windows C

API, for example - everything was an integer handle, long pointer or void pointer.

The biggest issue: the compiler did not check the type of a variable.

Page 13: Clean code _v2003

› With modern compilers AND modern programming practices:

String strName = “name”; // OBSOLETE, REDUNDANT, CLUTTERS UP THE CODE

Page 14: Clean code _v2003

Member prefixes

public class Part {private String m_dsc;

void setName(String description) {m_dsc = description;}

}

Page 15: Clean code _v2003

public class Part {String description; // What's wrong with this?

void setDescription(String description) { this.description = description;}

}

Page 16: Clean code _v2003

Avoid mental mapping

URL p; // an url used in the application

› Problems Readers shouldn't have to mentally translate

the name p into its meaning. A single-letter name is a poor choice; it’s just a

place holder that the reader must mentally map to the actual concept.

Page 17: Clean code _v2003

› One difference between a smart programmer and a PROFESSIONAL programmer is that the professional understands that clarity is king. Professionals write code that others can understand.

Page 18: Clean code _v2003

The first rule of functions:› SMALL

Functions should not be 100 lines long. Functions should hardly ever be 20 lines long. Smallness implies that blocks within if

statements, else statements, while statements, etc should be one line long, that line should be a function call.

functions should not be large enough to hold nested structures.

Page 19: Clean code _v2003

Therefore the indent level of a function should not be greater than one or two.

› This rule, of course, makes the functions easier to read and understand.

Page 20: Clean code _v2003

The second rule of functions:

› DO ONE THING We write functions to: decompose a larger

concept (i.e. the name of the function) into a set of steps at the next level of abstraction.

Page 21: Clean code _v2003

The third rule of functions:› NO MORE THAN THREE ARGUMENTS

the ideal number of arguments for a function is zero (niladic).

Next comes one (monadic), Followed closely by two (dyadic). Three arguments (triadic) should be avoided

where possible. More than three (polyadic) requires very

special justification, and then shouldn’t be used anyway.

Page 22: Clean code _v2003

Output Arguments› Arguments are most naturally interpreted as

inputs to a function.

appendFooter(s);

› To understand the code, we look at the method signature

public void appendFooter(StringBuffer report) { }

Page 23: Clean code _v2003

Command query separation

public boolean set(String attribute, String value) { }

› This leads to odd statements like this:

if (set("username", "unclebob")) { }

› What does this mean?

Page 24: Clean code _v2003

› Separate the preceding function in two, a command and a query:

if (attributeExists("username")) {setAttribute("username", "unclebob");

}

Page 25: Clean code _v2003

No side effectspublic class UserValidator {

private Cryptographer cryptographer;public boolean checkPassword(String userName, String password) {

User user = UserGateway.findByName(userName); if (user != User.NULL) { String codedPhrase = user.getPhraseEncodedByPassword(); String phrase = cryptographer.decrypt(codedPhrase, password); if ("Valid Password".equals(phrase)) {

Session.initialize(); return true;

}}return false;

}}

Page 26: Clean code _v2003

› Problem: the side effect represented by the call to Session.initialize().

Page 27: Clean code _v2003

Prefer Exceptions to Returning Error Codes

if (deletePage(page) == E_OK) {if (registry.deleteReference(page.name) == E_OK) {

if (configKeys.deleteKey(page.name.makeKey()) == E_OK){ logger.log("page deleted");} else { logger.log("configKey not deleted");}

} else {logger.log("deleteReference from registry failed");

}} else {

logger.log("delete failed");

Page 28: Clean code _v2003

› Problems: Returning error codes from command

functions is a subtle violation of command query separation: if (deletePage(page) == E_OK)

Leads to deeply nested structures.

Page 29: Clean code _v2003

› Possible refactoring

try {deletePage(page);registry.deleteReference(page.name);configKeys.deleteKey(page.name.makeKey());

}catch (Exception e) {

logger.log(e.getMessage());}

Page 30: Clean code _v2003

Comments Do Not Make Up for Bad Code

// Check to see if the employee is eligible for full benefitsif ((employee.flags && HOURLY_FLAG)

&& (employee.age > 65))

› What about this?

if (employee.isEligibleForFullBenefits())

Page 31: Clean code _v2003

Good comments› Informative comments

// format matched kk:mm:ss EEE, MMM dd, yyyyPattern timeMatcher = Pattern.compile(

"\\d*:\\d*:\\d* \\w*, \\w* \\d*, \\d*");

Page 32: Clean code _v2003

› Explanation of intent

public int compareTo(Object o){if(o instanceof WikiPagePath){ WikiPagePath p = (WikiPagePath) o; String compressedName = StringUtil.join(names, ""); String compressedArgumentName = StringUtil.join(p.names, ""); return compressedName.compareTo(

compressedArgumentName);}return 1; // we are greater because we are the right

type.}

Page 33: Clean code _v2003

› Warning of consequences// Don't run unless you// have some time to kill.public void _testWithReallyBigFile() { writeLinesToFile(10000000);}

› Amplification

Page 34: Clean code _v2003

Bad comments› Unclear explanation

public void loadProperties(){

try{ String propertiesPath = propertiesLocation + "/" + PROPERTIES_FILE; loadedProperties.load(propertiesPath);catch(IOException e){ // No properties files means all defaults are loaded}

}

Page 35: Clean code _v2003

› Redundant// Utility method that returns when this.closed is true. Throws an

// exception if the timeout is reached. public synchronized void waitForClose(final long timeoutMillis) throws Exception {

if(!closed){ wait(timeoutMillis); if(!closed) throw new Exception("MockResponseSender could not be

closed");}

}

Page 36: Clean code _v2003

// ContainerBase.java taken from Tomcatpublic abstract class ContainerBase

implements Container, Lifecycle, Pipeline, MBeanRegistration, Serializable {

/*** The processor delay for this component.*/protected int backgroundProcessorDelay = -1;

/*** The lifecycle event support for this component.*/protected LifecycleSupport lifecycle = new LifecycleSupport(this);

Page 37: Clean code _v2003

› Mandated Comments

/** * * @param title The title of the CD * @param author The author of the CD * @param tracks The number of tracks on the CD * @param durationInMinutes The duration of the CD in minutes */public void addCD(String title, String author, int tracks, int durationInMinutes) {

CD cd = new CD();cd.title = title;cd.author = author;cd.tracks = tracks;cd.duration = duration;cdList.add(cd);

}

Page 38: Clean code _v2003

› Noise comments

/** * Default constructor. //Oh, really? */

protected AnnualDateRule() {}

/** The day of the month. */private int dayOfMonth;

/** * Returns the day of the month. * * @return the day of the month. */public int getDayOfMonth() {return dayOfMonth;}

Page 39: Clean code _v2003

› Commented out code

InputStreamResponse response = new InputStreamResponse();response.setBody(formatter.getResultStream(),

formatter.getByteCount());// InputStream resultsStream = formatter.getResultStream();// StreamReader reader = new StreamReader(resultsStream);// response.setContent(reader.read(formatter.getByteCount()));

Why are those two lines of code commented? Are they important? Were they left as reminders? No one will delete them, they will rot in the code (sometimes for years).

Page 40: Clean code _v2003

Surprises coming up....

Page 41: Clean code _v2003
Page 42: Clean code _v2003
Page 43: Clean code _v2003
Page 44: Clean code _v2003
Page 45: Clean code _v2003
Page 46: Clean code _v2003

Urmeaza...poze de la manastire...