0% found this document useful (0 votes)
17 views

Clean Code

The document discusses principles of clean code, including that it should be elegant, simple, readable and work as expected. Clean code has meaningful names, avoids encodings and side effects, separates commands from queries, uses exceptions over error codes, and minimizes the need for comments through good structure and naming.

Uploaded by

calabi mozart
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
17 views

Clean Code

The document discusses principles of clean code, including that it should be elegant, simple, readable and work as expected. Clean code has meaningful names, avoids encodings and side effects, separates commands from queries, uses exceptions over error codes, and minimizes the need for comments through good structure and naming.

Uploaded by

calabi mozart
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 24

Clean Code

1 / 24
Clean Code

What is “clean code?”


I Elegant and efficient. – Bjarne Stroustrup
I Simple and direct. Readable. – Grady Booch
I Understandable by others, tested, literate. – Dave Thomas
I Code works pretty much as expected. Beatuful code looks like
the language was made for the problem. – Ward Cunningham
Why do we care abou clean code?
I Messes are costly. Quick and dirty to get it done ends up not
getting it done and you will not enjoy it. It’s lose-lose!
I We are professionals who care about our craft.

The Boy Scout Rule

2 / 24
Meaningful Names

I The name of a variable, method, or class should reveal its


purpose.
I If you feel the need to comment on the name itself, pick a
better name.
I Code with a dictionary close at hand.

Don’t ever do this!


1 int d; // elapsed time in days

Much better:
1 int elapsedTimeInDays;
2 int daysSinceCreation;
3 int daysSinceModification;
4 int fileAgeInDays;

3 / 24
Avoid Disinformative Names
Avoid names with baggage, unless you want the baggage.
I hpnot a good name for hypotenuse. hp could also be
Hewlett-Packard or horsepower.
Don’t hint at implementation details in a variable name.
I Prefer accounts to accountList.
I Note: certainly do want to indicate that a variable is a
collection by giving it a plural name.
Superbad: using O, 0, l, and 1.
1 int a = l;
2 if ( O == l )
3 a=O1;
4 else
5 l=01;

Don’t think you’ll never see code like this? Sadly, you will.

4 / 24
Avoid Encodings

Modern type systems and programming tools make encodings even


more unnecessary. So, AVOID ENCODINGS! Consider:
1 public class Part {
2 private String m_dsc; // The textual descriptio
3 void setName(String name) {
4 m_dsc = name;
5 }
6 }

The m_ is useless clutter. Much better to write:


1 public class Part {
2 private String description;
3 void setDescription(String description) {
4 this.description = description;
5 }
6 }

5 / 24
Clean Fuctions

Functions Should be Small and Do one Thing Only


How smallis small? A few lines, 5 or 10. “A screen-full” is no longer
meaningful with large monitors and small fonts.
Some signs a function is doing too much:
I “Sections” within a function, often delimited by blank lines.
I Deeply nested logic.
I Many parameters.
“If you have a procedure with ten parameters, you probably
missed some.” – Alan Perlis

6 / 24
Writing Functions that Do One Thing
One level of abstraction per function.
I A function that implements a higher-level algorithm should call
helper functions to execute the steps of the algorithm.
Write code using the stepdown rule.
I Code should read like a narrative from top to bottom.
I Read a higher level function to get the big picture, the
functions below it to get the details.
Example of stepdown rule/newspaper metaphor:
1 private void createGui() {
2 add(createDataEntryPanel(), BorderLayout.NORTH);
3 add(createButtonPanel(), BorderLayout.SOUTH);
4 setJMenuBar(createMenuBar());
5 }
6 private JPanel createDataEntryPanel() { ... }
7 private JPanel createButtonPanel() { ... }
8 private JMenuBar createMenuBar() { ... }

7 / 24
Function Parameters
Common one parameter forms
I Predicate functions: boolean fileExists“(”MyFile)
I Transformations: InputStream fileOpen“(”MyFile)
I Events: void passwordAttemptFailedNtimes(int attempts)

Higher numbers of function parameters are harder to get right. Even


one argument functions can be problematic. Consider flag argumets:
Instead of
I render(boolean isSuite), a call to which would look like
render(true),

write two methods, like


I renderForSuite() and renderForSingleTest()

Keep in mind that in OOP, every instance method call has an


implicit argument: the object on which it is invoked.
8 / 24
Minimizing the Number of Arguments

Use objects. Instead of


1 public void doSomethingWithEmployee(String name,
2 double pay,
3 Date hireDate)

Represent employee with a class:


1 public void doSomethingWith(Employee employee)

Use var-args for multiple parameters playing the same role:


1 public int max(int ... numbers)
2 public String format(String format, Object... args)

9 / 24
Avoid Side Effects

What’s wrong with this function?


1 public class UserValidator {
2 private Cryptographer cryptographer;
3 public boolean checkPassword(String userName, String password) {
4 User user = UserGateway.findByName(userName);
5 if (user != User.NULL) {
6 String codedPhrase = user.getPhraseEncodedByPassword();
7 String phrase = cryptographer.decrypt(codedPhrase, password);
8 if ("Valid Password".equals(phrase)) {
9 Session.initialize();
10 return true; }
11 }
12 return false; }
13 }

10 / 24
Avoid Side Effects

What’s wrong with this function?


1 public class UserValidator {
2 private Cryptographer cryptographer;
3 public boolean checkPassword(String userName, String password) {
4 User user = UserGateway.findByName(userName);
5 if (user != User.NULL) {
6 String codedPhrase = user.getPhraseEncodedByPassword();
7 String phrase = cryptographer.decrypt(codedPhrase, password);
8 if ("Valid Password".equals(phrase)) {
9 Session.initialize();
10 return true; }
11 }
12 return false; }
13 }

Has the side effect of initializing the session. Might erase an existing
session, or might create temporal coupling: can only check password
for user that doesn’t have an existing session.

11 / 24
Command Query Separation

Consider:
1 public boolean set(String attribute, String value);

We’re setting values and querying . . . something, leading to very


bad idioms like
1 if (set("username", "unclebob"))...

Better to separate commands from queries:


1 if (attributeExists("username")) {
2 setAttribute("username", "unclebob");
3 ...
4 }

12 / 24
Prefer Exceptions to Error Codes
Error codes force mixing of error handling with main logic :
1 if (deletePage(page) == E_OK) {
2 if (registry.deleteReference(page.name) == E_OK) {
3 if (configKeys.deleteKey(page.name.makeKey()) == E_OK){
4 logger.log("page deleted");
5 } else {
6 logger.log("configKey not deleted");
7 }
8 } else {
9 logger.log("deleteReference from registry failed"); }
10 } else {
11 logger.log("delete failed"); return E_ERROR;
12 }

Let language features help you:


1 try {
2 deletePage(page);
3 registry.deleteReference(page.name);
4 configKeys.deleteKey(page.name.makeKey());
5 } catch (Exception e) {
6 logger.log(e.getMessage());
7 }
13 / 24
Extract Try/Catch Blocks

You can make your code even clearer by extracting try/catch


statements into functions of their own:
1 public void delete(Page page) {
2 try {
3 deletePageAndAllReferences(page); }
4 catch (Exception e) {
5 logError(e);
6 }
7 }
8 private void deletePageAndAllReferences(Page page) throws Exception {
9 deletePage(page);
10 registry.deleteReference(page.name);
11 configKeys.deleteKey(page.name.makeKey());
12 }
13 private void logError(Exception e) {
14 logger.log(e.getMessage());
15 }

14 / 24
Clean Comments

Comments are (usually) evil.


I Most comments are compensation for failures to express ideas
in code.
I Comments become baggage when chunks of code move.
I Comments become stale when code changes.

Result: comments lie.


Comments don’t make up for bad code. If you feel the need for a
comment to explain some code, put effort into improving the code,
not authoring comments for it.

15 / 24
Good Names Can Obviate Comments

1 // Check to see if the employee is eligible for full benefits


2 if ((employee.flags & HOURLY_FLAG) && (employee.age > 65))

We’re representing a business rule as a boolean expression and


naming it in a comment. Use the language to express this idea:
1 if (employee.isEligibleForFullBenefits())

Now if the business rule changes, we know exactly where to change


the code that represents it, and the code can be reused. (What
does “reused” mean?)

16 / 24
Clean Formatting

Code should be written for human beings to understand,


and only incidentally for machines to execute. – Hal Abel-
son and Gerald Sussman, SICP
The purpose of a computer program is to tell other people
what you want the computer to do. – Donald Knuth

The purpose of formatting is to facilitate communication. The


formatting of code conveys information to the reader.

17 / 24
Vertical Formatting

I Newspaper metaphor
I Vertical openness between concepts Vertical density
I Vertical distance
I Vertical ordering

18 / 24
Vertical Openness Between Concepts
Notice how vertical openness helps us locate concepts in the code
more quickly.
1 package fitnesse.wikitext.widgets;
2
3 import java.util.regex.*;
4
5 public class BoldWidget extends ParentWidget {
6
7 public static final String REGEXP = "”’”’.+?";
8
9 private static final Pattern pattern = Pattern.compile("”’(.+?)”’",
10 Pattern.MULTILINE + Pattern.DOTALL
11 );
12
13 public BoldWidget(ParentWidget parent, String text) throws Exception {
14 super(parent);
15 Matcher match = pattern.matcher(text);
16 match.find();
17 addChildWidgets(match.group(1));
18 }
19 }

19 / 24
Vertical Openness Between Concepts
If we leave out the blank lines:
1 package fitnesse.wikitext.widgets;
2 import java.util.regex.*;
3 public class BoldWidget extends ParentWidget {
4 public static final String REGEXP = "”’”’.+?";
5 private static final Pattern pattern = Pattern.compile("”’(.+?)”’",
6 Pattern.MULTILINE + Pattern.DOTALL
7 );
8 public BoldWidget(ParentWidget parent, String text) throws Exception {
9 super(parent);
10 Matcher match = pattern.matcher(text);
11 match.find();
12 addChildWidgets(match.group(1));
13 }
14 }

I It’s harder to distinguish the package statement, the beginning


and end of the imports, and the class declaration.
I It’s harder to locate where the instance variables end and
methods begin.
20 / 24
Vertical Density
Openness separates concepts. Density implies association. Consider:
1 public class ReporterConfig {
2 /** The class name of the reporter listener */
3 private String className;
4
5 /** The properties of the reporter listener */
6 private List<Property> properties = new ArrayList<Property>();
7
8 public void addProperty(Property property) {
9 properties.add(property);
10 }

The vertical openness (and bad comments) misleads the reader.


Better to use closeness to convey relatedness:
1 public class ReporterConfig {
2 private String className;
3 private List<Property> properties = new ArrayList<Property>();
4
5 public void addProperty(Property property) {
6 properties.add(property);
7 }
8 }
21 / 24
Vertical Distance and Ordering

Concepts that are closely related should be vertically close to each


other.
I Variables should be declared as close to their usage as possible.
I Instance variables should be declared at the top of the class.
I Dependent functions: callers should be above callees.

22 / 24
Horizontal Openness and Density

I Keep lines short. Uncle Bob says 120, but he’s wrong. Keep
your lines at 80 characters or fewer if possible (sometimes it is
impossible, but very rarely).
I Put spaces around = to accentuate the distinction between the
LHS and RHS.
I Don’t put spaces between method names and parens, or parens
and paramter lists - they’re closely related, so should be close.
I Use spaces to accentuate operator precedence, e.g., no space
between unary operators and their operands, space between
binary operators and their operands.
I Don’t try to horizontally align lists of assignments – it draws
attention to the wrong thing and can be misleading, e.g.,
encouraging the reader to read down a column.
I Always indent scopes (classes, methods, blocks).

23 / 24
Team Rules

I Every team should agree on a coding standard and everyone


should adhere to it.
I Don’t modify a file just to change the formatting, but if you
are modifying it anyway, go ahead and fix the formatting of the
code you modify.
I Code formatting standards tend to get religious. My rule:
make your code look like the language inventor’s code.
I If the language you’re using has a code convention (like
Java’s), use it!

24 / 24

You might also like