Example: Sprout Method on TransactionGate
Starting code — postEntries posts a date on each entry and adds them to the bundle:
public class TransactionGate
{
public void postEntries(List entries) {
for (Iterator it = entries.iterator(); it.hasNext(); ) {
Entry entry = (Entry)it.next();
entry.postDate();
}
transactionBundle.getListManager().add(entries);
}
...
}We need to skip entries that are already in transactionBundle. The tempting inline approach folds the duplicate check into the existing loop:
public class TransactionGate
{
public void postEntries(List entries) {
List entriesToAdd = new LinkedList();
for (Iterator it = entries.iterator(); it.hasNext(); ) {
Entry entry = (Entry)it.next();
if (!transactionBundle.getListManager().hasEntry(entry) {
entry.postDate();
entriesToAdd.add(entry);
}
}
transactionBundle.getListManager().add(entriesToAdd);
}
...
}It works, but it’s invasive and we have no way to know we got it right. Worse, it muddies the method by mingling two concerns — date posting and duplicate detection — and introduces a temporary variable that tends to attract still more code over time.
The cleaner alternative treats duplicate removal as a separate operation, built test-first as a new method uniqueEntries:
public class TransactionGate
{
...
List uniqueEntries(List entries) {
List result = new ArrayList();
for (Iterator it = entries.iterator(); it.hasNext(); ) {
Entry entry = (Entry)it.next();
if (!transactionBundle.getListManager().hasEntry(entry) {
result.add(entry);
}
}
return result;
}
...
}Then the original method just calls it:
public class TransactionGate
{
...
public void postEntries(List entries) {
List entriesToAdd = uniqueEntries(entries);
for (Iterator it = entriesToAdd.iterator(); it.hasNext(); ) {
Entry entry = (Entry)it.next();
entry.postDate();
}
transactionBundle.getListManager().add(entriesToAdd);
}
...
}There’s still a temporary, but the method is much less cluttered, and any further work on the non-duplicated entries has a natural home (its own method, eventually its own class). The result is shorter, more understandable methods overall.
The Sprout Method steps
- Identify where the change goes.
- If it can be a single sequence of statements in one place, write the call to a new method that will do the work and comment it out (doing this first shows what the call will look like in context).
- Make the local variables the source method needs into arguments of the call.
- If the sprouted method returns a value, assign its return to a variable in the call.
- Develop the sprout method test-first (TDD).
- Uncomment the call to enable it.
Source
Working Effectively with Legacy Code, Michael Feathers — Chapter 6, I Don’t Have Much Time and I Have to Change It.