1
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)
Clean Architecture (Robert C. Martin)
The Clean Coder – The Code of Conduct for3 Professional Programmers (Robert C. Martin)
Refactoring – Improving the Design of Existing Code (Martin Fowler)
Domain Driven Design – Tackling Complexity in the Heart of Software (Eric Evans)
Test-Driven Development By Example (Kent Beck)
Extreme Programming Explained (Kent Beck)
Design Patterns Elements of Reusable Object Oriented Software (Erich Gamma)
Head First Design Patterns (Eric Freeman)
Agile Software Development Principles, Patterns and Practices (Robert C. Martin)
Spring Microservices In Action (John Carnell)
3
THERE WILL BE CODE
- Why do we need code? Soon all code will be generated
instead of written. We should be concerned about models
and requirements instead. Programmers simply4 won’t be
needed because business people will generate programs
from specifications.
- Nonsense! We will never get rid of code, because
code represents the details of the requirements. At
some level those details cannot be ignored or
abstracted; they have to be specified. And specifying
requirements in such detail that a machine can execute
them is programming. Such a specification is code.
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
“First, solve the problem. Then, write the code.”
– John Johnson
“Programming is the art of telling another human what one
wants the computer to do”
– Donald Knuth
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
if (age > 55) { int minRetirementAge = 55;
... 21 if (age > minRetirementAge) {
} ...
}
if (status == 2) { if (status > Status.ACTIVE)
... {
} ...
}
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))
private boolean isValidFileRequest(
25 isActiveFile, boolean isAdmin) {
String fileExtension, boolean
return (fileExtension == "mp4" ||
fileExtension == "mpg" ||
fileExtension == "avi") &&
(isAdmin || isActiveFile);
}
private boolean isValidFileRequest(
String fileExtension, boolean isActiveFile, boolean isAdmin) {
return isValidFileType(fileExtension) &&
isUserAllowedToViewFile(isAdmin, isActiveFile);
}
25
FUNCTIONS
We can describe the function by describing it as a brief TO paragraph:
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.
TO RenderPageWithSetupsAndTeardowns, we check to see whether the page is a test page
and if so, we include the setups and teardowns. In either case we render the page in
HTML.
26
FUNCTIONS – STEPDOWN RULE
Reading Code from Top to Bottom: The Stepdown Rule
To include the setups and teardowns, we
27include setups, then we include the test page
content,
and then we include the teardowns.
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)
Natural cohesion and natural ordering rule
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.
void monad(Integer... args);
void dyad(String name, Integer... args);
void triad(String name, int count, Integer... args);
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
public enum Error {
OK,
INVALID, 42
NO_SUCH,
LOCKED,
OUT_OF_RESOURCES,
WAITING_FOR_EVENT;
}
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);
}
}
private void deletePageAndAllReferences(Page page) throws Exception {…}
private void logError(Exception e) {…}
44
FUNCTIONS – DRY
45
45
FUNCTIONS – HOW DO WE WRITE THEM LIKE THIS?
46
46
FUNCTIONS – AVOID NULLPOINTER EXCEPTIONS
• Don’t return null
• Don’t pass null 47
47
FUNCTIONS – TYPES
• Procedural Programming - Functions \ Procedures
• OO Programming - Methods
48
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.*;
public class BoldWidget extends ParentWidget {
56
public static final String REGEXP = "'''.+?'''";
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();
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
private void serve() {
wife.give(fryingPan.getContents(20, PERCENT));
self.give(fryingPan.getContents(80, PERCENT)); // huehuehue
}
private void addEggs() { 59
fridge
.getEggs()
.forEach(egg -> fryingPan.add(egg.open());
}
private void cook() {
fryingPan.mixContents();
fryingPan.add(salt.getABit());
fryingPan.mixContents();
}
public void makeBreakfast() {
addEggs();
cook();
serve();
}
59
FORMATTING - VERTICAL DISTANCE
STEPDOWN RULE
public void makeBreakfast() {
addEggs();
cook();
serve();
}
60
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
}
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();
}
public interface Vehicle {
double getPercentFuelRemaining();
}
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.
An object should not expose internal
structure through accessors.
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
final String outputDir = ctxt.getOptions().getScratchDir().getAbsolutePath();
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
String outFile = outputDir + "/" + className.replace('.', '/') + ".class";
FileOutputStream fout = new FileOutputStream(outFile);
BufferedOutputStream bos = new BufferedOutputStream(fout);
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.
A class is closed, since it may be compiled, stored in a library,
baselined, and used by client classes. But it is also open, since any
new class may use it as parent, adding new features. When a
descendant class is defined, there is no need to change the original
or to disturb its clients.
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
Many client-specific interfaces are better than one general-purpose interface.
85
INTERFACE SEGREGATION
86
86
DEPENDENCY INVERSION
1. High-level modules should not depend on low-level modules. Both should
depend on abstractions. 87
2. Abstractions should not depend on details. Details should depend on
abstractions
87
THANK
88 YOU