Repeat with me...
● You can write very good code in a sea of legacy code
Slide 9
Slide 9 text
Repeat with me...
● You can write very good code in a sea of legacy code
● You can uglify code to be able to improve/test it
Slide 10
Slide 10 text
Repeat with me...
● You can write very good code in a sea of legacy code
● You can uglify code to be able to improve/test it
● This process is slow
Slide 11
Slide 11 text
Repeat with me...
● You can write very good code in a sea of legacy code
● You can uglify code to be able to improve/test it
● This process is slow
● Most of the time we are modifying code (and not adding)
Slide 12
Slide 12 text
1. Edit and Pray
2. Cover and Modify
Slide 13
Slide 13 text
Refactoring
Slide 14
Slide 14 text
Unit Tests
Slide 15
Slide 15 text
Strategies
1. Identify change points
2. Find test points
3. Break dependencies
4. Write tests
5. Make changes and refactor
Slide 16
Slide 16 text
Strategies
1. Identify change points
2. Find test points
3. Break dependencies
4. Write tests
5. Make changes and refactor
Slide 17
Slide 17 text
Seams
Slide 18
Slide 18 text
a place where you can
alter behavior without
modifying the code in
that place
Slide 19
Slide 19 text
Seams
● Preprocessing seams: with macros or plugins
Slide 20
Slide 20 text
Seams
● Preprocessing seams: with macros or plugins
● Link seams: with different libraries
Slide 21
Slide 21 text
Seams
● Preprocessing seams: with macros or plugins
● Link seams: with different libraries
● Object seams
Slide 22
Slide 22 text
Seams
● Preprocessing seams: with macros or plugins
● Link seams: with different libraries
● Object seams
● Every seam has an enabling point, a place where you can make the
decision to use one behavior or another.
Slide 23
Slide 23 text
Is this a seam?
cell.recalculate();
Slide 24
Slide 24 text
Seams
cell.recalculate();
Slide 25
Slide 25 text
This is not a seam
public void buildSheet() {
Cell cell = new FormulaCell(this, "", "");
cell.recalculate();
}
Slide 26
Slide 26 text
Seams
public void buildSheet(Cell cell) {
cell.recalculate();
}
Slide 27
Slide 27 text
Is this a seam?
public void buildSheet(Cell cell) {
recalculate(cell);
}
private static void recalculate(Cell cell) {
}
Slide 28
Slide 28 text
Break dependencies
1. Sensing: break dependencies to sense when we can’t access values our
code computes.
Slide 29
Slide 29 text
Break dependencies
1. Sensing: break dependencies to sense when we can’t access values our
code computes.
2. Separation: break dependencies to separate when we can’t even get a
piece of code into a test harness to run.
Slide 30
Slide 30 text
What if I don’t want to
write tests?
Slide 31
Slide 31 text
What I don’t want to write tests...
● Sprout method
Slide 32
Slide 32 text
Sprout method
public void postEntries(List entries) {
for (Entry entry : entries) {
entry.postDate();
}
transaction.getListManager().addAll(entries);
}
Slide 33
Slide 33 text
Sprout method
public void postEntries(List entries) {
List entriesToAdd = new LinkedList();
for (Entry entry : entries) {
if(!transaction.getListManager().contains(entry)) {
entry.postDate();
entriesToAdd.add(entry);
}
}
transaction.getListManager().addAll(entriesToAdd);
}
Slide 34
Slide 34 text
Sprout method
public void postEntries(List entries) {
for (Entry entry : entries) {
entry.postDate();
}
transaction.getListManager().addAll(entries);
}
Slide 35
Slide 35 text
Sprout method
public void postEntries(List entries) {
List filteredEntries = uniqueEntries(entries);
for (Entry entry : filteredEntries) {
entry.postDate();
}
transaction.getListManager().addAll(filteredEntries);
}
Slide 36
Slide 36 text
What I don’t want to write tests...
● Sprout method
● Sprout class
Slide 37
Slide 37 text
What I don’t want to write tests...
● Sprout method
● Sprout class
● Wrap method
Slide 38
Slide 38 text
Wrap method
public void pay() {
Money amount = new Money();
for (Timecard card : timecards) {
if (payPeriod.contains(date)) {
amount.add(card.getHours() * payRate);
}
}
payDispatcher.pay(this, date, amount);
}
Slide 39
Slide 39 text
Wrap method
public void dispathPayment() {
Money amount = new Money();
for (Timecard card : timecards) {
if (payPeriod.contains(date)) {
amount.add(card.getHours() * payRate);
}
}
payDispatcher.pay(this, date, amount);
}
Slide 40
Slide 40 text
Wrap method
public void pay() {
logPayment();
dispatchPayment();
}
Slide 41
Slide 41 text
What I don’t want to write tests...
● Sprout method
● Sprout class
● Wrap method
● Wrap class
Slide 42
Slide 42 text
What I don’t want to write tests...
● Sprout method
● Sprout class
● Wrap method
● Wrap class
● Follow dependency tips
Slide 43
Slide 43 text
FAQ
Slide 44
Slide 44 text
I don’t understand the code to change it
● Read it many times (d’oh!)
● Do sketches
● Extract small methods
● Scratch Refactoring
Slide 45
Slide 45 text
Class too big and I don’t want it to get any bigger
● Divide responsibilities (techniques!)
Slide 46
Slide 46 text
Class too big and I don’t want it to get any bigger
● Divide responsibilities (techniques!)
● Look for group methods
Slide 47
Slide 47 text
Class too big and I don’t want it to get any bigger
● Divide responsibilities (techniques!)
● Look for group methods
● Look at hidden methods
Slide 48
Slide 48 text
Class too big and I don’t want it to get any bigger
● Divide responsibilities (techniques!)
● Look for group methods
● Look at hidden methods
● Look for decisions that can change together
Slide 49
Slide 49 text
Class too big and I don’t want it to get any bigger
● Divide responsibilities (techniques!)
● Look for group methods
● Look at hidden methods
● Look for decisions that can change together
● Look for internal relationships
Slide 50
Slide 50 text
Class too big and I don’t want it to get any bigger
● Divide responsibilities (techniques!)
● Look for group methods
● Look at hidden methods
● Look for decisions that can change together
● Look for internal relationships
● Look for the primary responsibility of the class
Slide 51
Slide 51 text
Class too big and I don’t want it to get any bigger
● Divide responsibilities (techniques!)
● Look for group methods
● Look at hidden methods
● Look for decisions that can change together
● Look for internal relationships
● Look for the primary responsibility of the class
● Do some scratch refactoring
Slide 52
Slide 52 text
Class too big and I don’t want it to get any bigger
● Divide responsibilities (techniques!)
● Look for group methods
● Look at hidden methods
● Look for decisions that can change together
● Look for internal relationships
● Look for the primary responsibility of the class
● Do some scratch refactoring
● Focus on the current work
Slide 53
Slide 53 text
My application is all API calls
● Skin and wrap the API
● Responsibility-Based Extraction
Slide 54
Slide 54 text
My application has no structure
● Tell the story of the system.
Slide 55
Slide 55 text
What methods should I test?
● Do characterization tests
Slide 56
Slide 56 text
What methods should I test?
● Do characterization tests
● Sketches
Slide 57
Slide 57 text
What methods should I test?
● Do characterization tests
● Sketches
● Look for interception and pinch points
Slide 58
Slide 58 text
Tricks
Slide 59
Slide 59 text
Tricks
● Use DI, fakes, nulls, null objects, extract interfaces... to separate
dependencies
● Preserve signatures
Slide 60
Slide 60 text
Tricks
● Minimize coupling points when extracting: the number of values that
pass into and out of the method
● Do extractions with these goals:
1. To separate logic from dependencies
2. To introduce seams
Slide 61
Slide 61 text
Tricks
● Do 1 thing at a time
Slide 62
Slide 62 text
Mars Rover
Slide 63
Slide 63 text
Techniques
Slide 64
Slide 64 text
Techniques
● Adapt Parameter: change signature to pass a parameter
public void populate(HttpServletRequest request) {
String[] values = request.getParameterValues( pageStateName);
if (values != null && values.length > 0) {
marketBindings.put(pageStateName + getDateStamp(), values[ 0]);
}
}
Slide 65
Slide 65 text
Techniques
● Adapt Parameter: change signature to pass a parameter
public void populate(ParameterSource source) {
String[] values = source.getParameterForName(pageStateName);
if (values != null && values.length > 0) {
marketBindings.put(pageStateName + getDateStamp(), values[ 0]);
}
}
Slide 66
Slide 66 text
Techniques
● Adapt Parameter: change signature to pass a parameter
● Break Out Method Object: create a class with the method
Slide 67
Slide 67 text
Techniques
● Adapt Parameter: change signature to pass a parameter
● Break Out Method Object: create a class with the method
● Encapsulate Global References
Slide 68
Slide 68 text
Techniques
● Adapt Parameter: change signature to pass a parameter
● Break Out Method Object: create a class with the method
● Encapsulate Global References
● Expose Static Method
Slide 69
Slide 69 text
Techniques
● Adapt Parameter: change signature to pass a parameter
● Break Out Method Object: create a class with the method
● Encapsulate Global References
● Expose Static Method
● Extract and Override Call / Factory Method / Getter
Techniques
● Extract and Override Call / Factory Method / Getter
protected void rebindStyles() {
styles = formStyles(template, id);
}
public class TestingPageLayout extends Hello {
protected List formStyles(StyleTemplate template, int id) {
return new ArrayList();
}
}
Slide 72
Slide 72 text
Techniques
● Adapt Parameter: change signature to pass a parameter
● Break Out Method Object: create a class with the method
● Encapsulate Global References
● Expose Static Method
● Extract and Override Call / Factory Method / Getter
● Extract interface
Slide 73
Slide 73 text
Techniques
● Adapt Parameter: change signature to pass a parameter
● Break Out Method Object: create a class with the method
● Encapsulate Global References
● Expose Static Method
● Extract and Override Call / Factory Method / Getter
● Extract interface
● Introduce Instance Delegator