1. How to write clean code
using TDD
Srinivasa GV
srinivasa.gv@sap.com
SAP Labs India
2. Agenda
1. What is Clean Code ?
2. Some basics on Clean Code
3. What is TDD?
4. Fundamentals of TDD
5. Demo of TDD with Clean Code
3. 3
Some definitions of Clean Code 1
Dave Thomas – godfather of the Eclipse
strategy: Clean code can be read, and
enhanced by a developer other than its
original author. It has unit and acceptance
tests. It has meaningful names.
1 Robert C. Martin Clean Code Prentice Hall
4. 4
Thought leaders definition…
Grady Booch – author of OOAD with
applications: Clean code is simple and
direct. Clean code reads like well-written
prose and never obscures the designer’s
intent
6. 6
Why Clean Code ? – A story
Ram opens the project and enters the class.
He scrolls down to the method needing change.
He pauses, considering his options.
Oh, he’s scrolling up to the top of the function to check the initialization of a variable.
Now he scrolls back down and begins to type. Ooops, he’s erasing what he typed!
He types it again.
He erases it again!
He types half of something else but then erases that!
7. 7
..
He scrolls down to another function that calls the function he’s changing to see
how it is called.
He scrolls back up and types the same code he just erased.
He pauses.
He erases that code again!
He pops up another window and looks at a subclass. Is that function
overridden?
Reading vs Writing ratio is > 10:1
9. 9
Meaningful Names
Class Names
Classes and objects should have noun or noun phrase names
like Customer, WikiPage, Account, and AddressParser.
Avoid words likeManager, Processor, Data, or Info in the name of a class. A class name
should not be a verb.
Method Names
Methods should have verb or verb phrase names like postPayment, deletePage, or save.
Accessors, mutators, and predicates should be named for their value and prefixed
with get, set, and is according to the javabean standard.[4]
[4] http://java.sun.com/products/javabeans/docs/spec.html
10. 10
Functions/Methods
One level of abstraction per Function:
Functions should do one thing. They should do it well. They should do it only.
Functions need to be short, well named and nicely organized.
11. 11
An example
Bad Code Good Code
HTMLUtil.java
public static String testableHtmlContent(
PageData pageData,
boolean includeSuiteSetup
) throws Exception {
WikiPage wikiPage = pageData.getWikiPage();
StringBuffer buffer = new StringBuffer();
if (pageData.hasAttribute("Test")) {
if (includeSuiteSetup) {
WikiPage suiteSetup =
PageCrawlerImpl.getInheritedPage(
SuiteResponder.SUITE_SETUP_NAME, wikiPage
);
if (suiteSetup != null) {
WikiPagePath pagePath =
suiteSetup.getPageCrawler().getFullPath(suiteSetup);
String pagePathName = PathParser.render(pagePath);
buffer.append("!include -setup .")
.append(pagePathName)
.append("n");
}
}
WikiPage setup =
PageCrawlerImpl.getInheritedPage("SetUp", wikiPage);
if (setup != null) {
WikiPagePath setupPath =
wikiPage.getPageCrawler().getFullPath(setup);
String setupPathName = PathParser.render(setupPath);
buffer.append("!include -setup .")
.append(setupPathName)
……
……....
………………………………..ANOTHER 40 more lines
HTMLUtil.java
public static String renderPageWithSetupsAndTeardowns(
PageData pageData, boolean isSuite
) throws Exception {
boolean isTestPage = pageData.hasAttribute("Test");
if (isTestPage) {
WikiPage testPage = pageData.getWikiPage();
StringBuffer newPageContent = new StringBuffer();
includeSetupPages(testPage, newPageContent, isSuite);
newPageContent.append(pageData.getContent());
includeTeardownPages(testPage, newPageContent, isSuite);
pageData.setContent(newPageContent.toString());
}
return pageData.getHtml();
}
ANOTHER 3 MORE FUNCTIONS
includeSetupPages,
newPageContnet,
includeTearDownPages
12. 12
Comments
Try to avoid comments except for absolute minimum required
Explain Yourself in Code – write expressive code
// Check to see if the student is eligible for full marks
if ((student.flags & YEARLY_FLAG) &&
(student.attendance > 200))
if (student.isEligibleForFullMarks())
13. 13
Formatting
Code formatting is important – it’s because code communicates
Vertical Formatting - The topmost parts of the source file should provide the high-
level concepts and algorithms (Vertical openness, Vertical Density, Vertical
Distance, Vertical Ordering)
Horizontal Formatting (Horizontal openness and Density, Indentation)
14. 14
An example for vertical openness between concepts
Good Code Bad Code
package fitnesse.wikitext.widgets;
import java.util.regex.*;
public class BoldWidget extends ParentWidget {
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();
}
}
package fitnesse.wikitext.widgets;
import java.util.regex.*;
public class BoldWidget extends ParentWidget {
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();
}
}
15. 15
Classes
Classes Should Be Small
It’s not about the number of lines, but about the number of responsibilities
Classes should have one responsibility—one reason to change.
Cohesion
Classes should have a small number of instance variables. Each of the methods of
a class should manipulate one or more of those variables.
Follow S.O.L.I.D principles (Single responsibility, Open/Closed, Liskov
Substitution, Interface Segregation, Dependency Injection)
16. 16
An example for Single Responsiblity
Source:
http://www.globalnerdy.com
17. Test Driven Development
“Accountants practice Dual Entry Bookkeeping as part of the GAAP1
TDD is Dual Entry Bookkeeping for software”
Robert C Martin
18. Test First – Test Driven Development
TDD Cycle
1. Write a failing test
2. Write the code to pass the test
3. Clean up / Refactor
(either production or test code)
Start
Test
Code
Test
Refactor
20. Test Driven Development Demo
User Story:
Roman to Arabic Numerals Conversion
As a user who imports external documents into the system I want the system to convert
Roman numerals into Arabic values (e.g. year numerals or list numbers) so that searching
and ordering is possible.
Additional Info:
The system shall deal with roman numerals as described in wikipedia
The system shall deal with the range from I (1) to MMM (3000)
Acceptance Criteria:
The system will convert MCMLXXXIV to 1984
21. TDD Benefits I
Focus on Essentials since
tests codify requirements
test drives feature implementation
you build the simplest thing that is
needed right now
Confidence
safety net
encourages changes
bug repellent
22. TDD Benefits II
Better Code Quality
Consumable API
Decoupling and testability
Living Technical Documentation
Tests as executable specification
Tests document usage of API
23. Which benefits did you observe?
Expected:
Test driven development –
– tests and productive coding are created in parallel – in small steps ; steps are like how we analyze the problem
– 100% coverage by default
– TDD Cycle: Green – Red – Green – Refactor
– debugger wasn’t required
– Self validating tests
Code Quality
– Easy to read
Continuous Integration
– Software was executable all the time
Refactoring
– Avoid Technical Debt
– The refactoring to get checks for invalid numbers was always secured by existing unit tests.
25. 25
References
Test – Driven Development By example – Kent Beck
Clean Code A Handbook of Agile Software Craftsmanship – Robert C. Martin
Refactoring Improving the design of Existing Code – Martin Fowler