Clean Code
Clean Code
CLEAN CODE
CONTENTS
THERE WILL BE CODE
NAMING 2
FUNCTIONS
COMMENTS
FORMATTING
OBJECTS AND DATA STRUCTURES
CLASSES AND SOLID PRINCIPLES
2
RESOURCES
Clean Code – A Handbook of Agile Software Craftsmanship (Robert C. Martin)
4
WHY SHOULD I CARE?
Readability5 Flexibility
Efficiency Maintainability
Empathy
5
WRITING CODE IS THE ART OF WRITING A BOOK
“Any fool can write code that a computer can understand.
Good programmers write code that humans can understand.”
– Martin Fowler
6
”Code is like humor. When you have to explain it, it’s bad.”
– Cory House
6
CLEAN CODE IS FOUNDATION
TDD
DDD
Design
Patter
7 ns
Refactoring
SOLID Code
Clean Code
7
THE MESS BUILDS QUIETLY
8
THE MESS BUILDS QUIETLY
A book or a newspaper article Well organized code
9
Paragraph Method
1
Heading1 1
Class1
=
Paragraph Method
Chapter 2 Packag 2
Paragraph e
Heading2 Method
1 Class2
1
9
CODE QUALITY MEASUREMENT
10
10
THE TOTAL COST OF OWNING A MESS
11
11
THE TOTAL COST OF OWNING A MESS
12
12
THE GRAND REDESIGN IN THE SKY
13
13
ATTITUDE
14
14
THE BOY SCOUT RULE
The Boy Scouts of America have a simple rule that we can apply to our
profession.
15
Leave the campground cleaner than you found it.
or
Leave the code cleaner than you found it.
15
MEANINGFUL NAMES
• Use intention-revealing names
• Avoid disinformation
• Make meaningful distinctions
16
• Use pronounceable names (avoid abbreviations)
• Use searchable names
• Avoid encodings
• Avoid mental mapping
• Class names (noun or noun phrase e.g. Customer)
• Method names (verb or verb phrase e.g. save)
• Pick one word per concept (fetch, retrieve and get equivalent for different classes?)
• Use solution domain names (use computer science terms, algorithm names, pattern
terms, math terms e.g. AccountVisitor = Visitor pattern)
• Use problem domain names
16
MEANINGFUL NAMES – WARNING SIGNS
• Watch out for:
• AND IF OR
17
• Avoid abbreviations
• Booleans
• Dirty: open, start, status, login
• Clean: isOpen, done, isActive, loggedIn
17
CONDITIONALS – BE POSITIVE
DON’T BE ANTI-NEGATIVE
18
if (!isNotNumber())
if (isNumber())
18
CONDITIONALS – TERNARY OPERATOR ELEGANCE
if (a < b) {
minVal = a; 19
} else {
minVal = b;
}
minVal = (a < b) ? a : b;
19
CONDITIONALS - AVOID BEING “STRINGLY” TYPED
BE STRONGLY TYPED
20
if(user.getType() == "admin")
if(user.getType() == UserType.ADMIN)
20
CONDITIONALS – AVOID MAGIC NUMBERS
21
CONDITIONALS – INTERMEDIATE VARIABLES
if (employee.getAge() > 55 &&
employee.getYearsEmployed() > 10 &&
employee.isRetired()) 22
{
...
}
22
CONDITIONALS – INTERMEDIATE VARIABLES
int minRetirementAge = 55;
int minPensionEmploymentYears 23
= 10;
boolean eligibleForPension = employee.getAge() > minRetirementAge &&
employee.getYearsEmployed > minPensionEmploymentYears &&
employee.isRetired()
if (eligibleForPension) {
...
}
23
CONDITIONALS – ENCAPSULATE COMPLEX
CONDITIONALS
if ((fileExtension == "mp4" ||
fileExtension == "mpg" ||
24 &&
fileExtension == "avi")
(isAdmin || isActiveFile)) {
...
}
24
CONDITIONALS – ENCAPSULATE COMPLEX
CONDITIONALS
if (isValidFileRequest(fileExtension, isActiveFile, isAdmin))
The LOGO language used the keyword “TO” in the same way that Ruby and Python use “def.” So
26
every function began with the word “TO.” This had an interesting effect on the way functions were
designed.
26
FUNCTIONS – STEPDOWN RULE
Reading Code from Top to Bottom: The Stepdown Rule
To include the setups, we include the suite setup if this is a suite, then we include the
regular setup.
To include the suite setup, we search the parent hierarchy for the “SuiteSetUp” page
and add an include statement with the path of that page.
To search the parent. . .
27
FUNCTIONS – SMALL!
1. Functions should be small
2. They should be smaller than that
28
In the 80’s we used to say that a function should be no bigger than a screen-full.
(VT100 screens were 24 lines by 80 columns)
28
FUNCTIONS – DO ONE THING
FUNCTIONS SHOULD DO ONE THING. THEY SHOULD DO IT WELL.
THEY SHOULD DO IT ONLY.
29
29
FUNCTIONS – MIXED LEVELS OF ABSTRACTION
public void makeBreakfast() {
addEggs();
cook();
30
wife.give(fryingPan.getContents(20, PERCENT));
self.give(fryingPan.getContents(80, PERCENT)); // huehuehue
}
private void addEggs() {
fridge
.getEggs()
.forEach(egg -> fryingPan.add(egg.open());
}
private void cook() {
fryingPan.mixContents();
fryingPan.add(salt.getABit());
fryingPan.mixContents();
}
30
FUNCTIONS – MIXED LEVELS OF ABSTRACTION
public void makeBreakfast() {
addEggs();
cook();
serve();
31
}
private void addEggs() {
fridge
.getEggs()
.forEach(egg -> fryingPan.add(egg.open());
}
private void cook() {
fryingPan.mixContents();
fryingPan.add(salt.getABit());
fryingPan.mixContents();
}
private void serve() {
wife.give(fryingPan.getContents(20, PERCENT));
self.give(fryingPan.getContents(80, PERCENT)); // huehuehue
}
31
FUNCTIONS – WHEN TO CREATE
Duplicatio Extract
n method
Excessive
32
Return
Indentatio
early
n
Unclear
Function Fail Fast
intent
> 1 task
Flag
32
arguments
FUNCTIONS – EXCESSIVE INDENTATION
ARROW SYMPTOM
if (...) {
33 if (...) {
if (...) {
switch (...) {
...
}
}
}
}
33
FUNCTIONS – HOW MANY ARGUMENTS ?
Strive to 0 – 2
> 3 params ?
34
Primitive Obsession – Encapsulate into Class, group the params
Raise params to instance level (method params will be class’ fields) by using
current class
or
create a separate class for this purpose (Extract Class refactoring technique)
34
FUNCTIONS - ARGUMENTS
• Monadic
boolean fileExists("MyFile");
35
• Flag arguments
render(true)
render(boolean isSuite)
renderForSuite() and renderForSingleTest()
• Dyadic
writeField(outputStream, name)
• Triads
assertEquals(message, expected, actual)
35
FUNCTIONS - ARGUMENTS
• Argument Objects
Circle makeCircle(double x, double y, double radius);
Circle makeCircle(Point center, double radius);
36
• Argument Lists
String.format("%s worked %.2f hours.", name, hours);
public String format(String format, Object... args);
Functions that take variable arguments can be monads, dyads, or even triads.
36
FUNCTIONS – GOOD NAMES
Choosing good names for a function can go a long way toward
• explaining the intent of the function
• and the order and intent of the arguments
37
write(name);
writeField(name);
assertEquals(expected, actual);
assertExpectedEqualsActual(expected, actual);
37
FUNCTIONS – HAVE NO SIDE EFFECTS
Side effects are lies.
Your function promises to do one thing, but it also does other hidden things.
38
38
FUNCTIONS – NO OUTPUT ARGUMENTS
• appenHeader(s);
39
• public void appendHeader(StringBuffer report);
• report.appendHeader();
39
FUNCTIONS – COMMAND QUERY SEPARATION
• public boolean set(String attribute, String value);
40
• if (set("username", "unclebob"))... ???
• if (attributeExists("username")) {
setAttribute("username", "unclebob");
...
}
40
FUNCTIONS – 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){
41
logger.log("page deleted");
} else {
logger.log("configKey not deleted");
}
} else {
logger.log("deleteReference from registry failed");
}
} else {
logger.log("delete failed");
return E_ERROR;
}
41
FUNCTIONS – ERROR.JAVA DEPENDENCY MAGNET
42
FUNCTIONS – EXTRACT TRY/CATCH BLOCKS
try {
deletePage(page); 43
registry.deleteReference(page.name);
configKeys.deleteKey(page.name.makeKey());
} catch (Exception e) {
logger.log(e.getMessage());
}
43
FUNCTIONS – EXTRACT TRY/CATCH BLOCKS
public void delete(Page page) {
try {
44
deletePageAndAllReferences(page);
}
catch (Exception e) {
logError(e);
}
}
44
FUNCTIONS – DRY
45
45
FUNCTIONS – HOW DO WE WRITE THEM LIKE THIS?
46
46
FUNCTIONS – AVOID NULLPOINTER EXCEPTIONS
47
FUNCTIONS – TYPES
48
SOFTWARE DEVELOPMENT PRINCIPLES
49
DRY
YAGNI and
WET
TMTWTDI
KISS
49
COMMENTS
“Don’t comment bad code - rewrite it.”
Brian W. Kernighan and P.50J. Plaugher
50
COMMENTS – EXPLAIN YOURSELF IN CODE
// Check to see if the employee is eligible for full benefits
if ((employee.flags & HOURLY_FLAG) &&
(employee.age > 65)) 51
if (employee.isEligibleForFullBenefits())
51
COMMENTS – GOOD COMMENTS
AVOID THEM WHERE POSSIBLE
Informative Comments
Explanation of Intent
Clarification 52
Warning of Consequences
TODO Comments
Amplification
Javadocs in Public APIs
52
COMMENTS – BAD COMMENTS
Mumbling
Redundant Comments
Misleading Comments
Mandated Comments 53
Journal Comments
Noise Comments
Scary Noise
Don’t Use a Comment When You Can Use a Function or a Variable
Position Markers
Closing Brace Comments
Attributions and Bylines
Commented-Out Code
Nonlocal Information
Too Much Information
Function Headers
Javadocs in Nonpublic Code
53
FORMATTING
54
54
FORMATTING
55
55
FORMATTING – VERTICAL OPENNESS
BETWEEN CONCEPTS
package fitnesse.wikitext.widgets;
import java.util.regex.*;
addChildWidgets(match.group(1));
}
return html.toString();
56 }
}
FORMATTING – VERTICAL OPENNESS
BETWEEN CONCEPTS
package fitnesse.wikitext.widgets;
import java.util.regex.*;
public class BoldWidget extends ParentWidget {
public static final String REGEXP = "'''.+?'''";
57
private static final Pattern pattern = Pattern.compile("'''(.+?)'''",
Pattern.MULTILINE + Pattern.DOTALL);
public BoldWidget(ParentWidget parent, String text) throws Exception {
super(parent);
Matcher match = pattern.matcher(text);
match.find();
addChildWidgets(match.group(1));}
public String render() throws Exception {
StringBuffer html = new StringBuffer("<b>");
html.append(childHtml()).append("</b>");
return html.toString();
}
}
57
FORMATTING – VERTICAL DISTANCE
Variable declarations (Mayfly variables)
Instance variables
Dependent Functions 58
Conceptual Affinity
58
FORMATTING - VERTICAL DISTANCE
STEPDOWN RULE
59
FORMATTING - VERTICAL DISTANCE
STEPDOWN RULE
60
TEAM RULES
61
61
OBJECTS AND DATA STRUCTURES
• Data Structures (expose data, getters and setters)
• Objects (hide data and expose
62 behaviour)
• Hybrids (data + behaviour)
62
DATA ABSTRACTION
public class Point { public interface Point {
public double x; double getX();
public double y; 63
double getY();
} void setCartesian(double x, double y);
double getR();
double getTheta();
void setPolar(double r, double theta);
}
63
DATA ABSTRACTION
public interface Vehicle {
double getFuelTankCapacityInGallons();
64
double getGallonsOfGasoline();
}
64
DATA/OBJECT ANTI-SYMMETRY
public class Square {
public Point topLeft;
public double side; 65
}
public class Rectangle {
public Point topLeft;
public double height;
public double width;
}
public class Circle {
public Point center;
public double radius;
}
65
DATA/OBJECT ANTI-SYMMETRY
public class Geometry {
public final double PI = 3.141592653589793;
public double area(Object shape) throws NoSuchShapeException
{
66
if (shape instanceof Square) {
Square s = (Square)shape;
return s.side * s.side;
} else if (shape instanceof Rectangle) {
Rectangle r = (Rectangle)shape;
return r.height * r.width;
} else if (shape instanceof Circle) {
Circle c = (Circle)shape;
return PI * c.radius * c.radius;
}
throw new NoSuchShapeException();
}
66
}
DATA/OBJECT ANTI-SYMMETRY
public class Square implements Shape {
private Point topLeft;
private double side; 67
public double area() {
return side*side;
}
}
public class Rectangle implements Shape {
private Point topLeft;
private double height;
private double width;
public double area() {
return height * width;
}
67
}
DATA/OBJECT ANTI-SYMMETRY
public class Circle implements Shape {
private Point center;
private double radius; 68
public final double PI = 3.141592653589793;
public double area() {
return PI * radius * radius;
}
}
68
DATA/OBJECT ANTI-SYMMETRY
Procedural Code – easy to add new functions without changing the existing data structures
OO Code – easy to add new classes without changing existing functions
69
The complement is also true:
Procedural Code – hard to add new data structures because all the functions must change
OO Code – hard to add new functions because all the classes must change
69
LAW OF DEMETER
A module should not know about the innards
70
of the objects it manipulates.
70
LAW OF DEMETER
A method f of a class C should only call the methods of these:
71
• C
• An object created by f
• An object passed as an argument to f
• An object held in an instance variable of C
71
LAW OF DEMETER
72
72
TRAIN WRECKS
Options opts = ctxt.getOptions();
File scratchDir = opts.getScratchDir();
73
final String outputDir = scratchDir.getAbsolutePath();
73
HIDING STRUCTURE
ctxt.getAbsolutePathOfScratchDirectoryOption();
or 74
ctx.getScratchDirectoryOption().getAbsolutePath();
74
HIDING STRUCTURE
ctxt.getAbsolutePathOfScratchDirectoryOption();
... 75
75
HIDING STRUCTURE
BufferedOutputStream bos = ctxt.createScratchFileStream(classFileName);
76
76
HYBRIDS
Hybrid = Object + Data Structure
77
• Hard to add new functions
• Hard to add data structures
77
DATA STRUCTURE TYPES
• Data Transfer Objects
• Active Record 78
78
CLASSES
Classes Should Be Small!
79
79
CLASSES – SINGLE RESPONSIBILITY PRINCIPLE
80
80
CLASSES – COHESION SHOULD BE HIGH
• Classes should have a small number of instance variables.
• Each of the methods of a class should manipulate one or more of those
81
variables.
81
SOLID
82
82
OPEN / CLOSED
Software entities (classes, modules, functions, etc.) should be
open for extension, but closed83for modification.
83
LISKOV SUBSTITUTION
Let Φ(x) be a property provable about objects x of type T.
Then Φ(y)should be true for objects y of
84
type S where S is a subtype
of T.
84
INTERFACE SEGREGATION
Clients should not be forced to depend upon interfaces that they do
not use. 85
85
INTERFACE SEGREGATION
86
86
DEPENDENCY INVERSION
1. High-level modules should not depend on low-level modules. Both should
depend on abstractions. 87
87
THANK
88 YOU