Working Effectively With Legacy Code — Mechanics of Change(Part II: Chapter 1)

PART II: Changing Software

Chapter 1: I Don’t Have Much Time and I Have to Change It.

It Happens Someplace Every DayYour boss comes in. He says, “Clients are clamoring for this feature. Can we get it done today?”“I don’t know.”
You look around. Are there tests in place? No. You ask, “How bad do you need it?”
You know that you can make the changes inline in all 10 places where you need to change things, and it will be done by 5:00. This is an emergency right? We’re going to fix this tomorrow, aren’t we?Remember, code is your house, and you have to live in it.
  • Sprout Method.
  • Sprout Class.
  • Wrap Method.
  • Wrap Class.
public class TransactionGate {public void postEntries(List entries) {
List entriesToAdd = new LinkedList();
for (Iterator it = entries.iterator(); it.hasNext(); ) {
entry.postDate();
entriesToAdd.add(entry);
}
transactionBundle.getListManager().add(entriesToAdd); }
}
if (!transactionBundle.getListManager().hasEntry(entry) {                 
entry.postDate();
entriesToAdd.add(entry);
}
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;
}
public class TransactionGate {public void postEntries(List entries) {
List entriesToAdd = uniqueEntries(entries);
for (Iterator it = entries.iterator(); it.hasNext(); ) {
entry.postDate();
entriesToAdd.add(entry);
}

transactionBundle.getListManager().add(entriesToAdd); }
}
  • With Sprout Method, we are using a clear separated new code from old code with a test one.
  • The downside of Sprout Method is that we are just adding some new functionality without making the old class any better.
class QuarterlyReportGenerator {
.....

void generate() {
Result results = database.queryResults(beginDate, endDate);
String pageText;
pageText += "<html><head><title> Quarterly Report </title></head><body><table>";
if (results.size() != 0) {
for (int i = results.begin(); i != results.end(); ++i) {
pageText += "<tr>";
pageText += "<td>" + i -> department + "</td>";
pageText += "<td>" + i -> manager + "</td>";
char buffer[ 128];
sprintf(buffer, "<td>$%d</td>", i -> netProfit / 100);
pageText += string(buffer);
sprintf(buffer, "<td>$%d</td>", i -> operatingExpense / 100);
pageText += string(buffer);
pageText += "</tr>";
}
}
}
......
}
class TableHeaderProducer {

void makeHeader() {
return "<tr><td>Department</td><td>Manager</td> < td > Profit </td ><td > Expenses </td >";
}

}
TableHeaderProducer producer = new TableHeaderProducer();
pageText += producer.makeHader();
  • It allows us to move forward with more confidence without invasive changes.
  • The key disadvantage of Sprout Class is the conceptual complexity of implementing it.
public class Employee {
...

public void pay() {
Money amount = new Money();
for (Iterator it = timecards.iterator(); it.hasNext(); ) {
Timecard card = (Timecard) it.next();
if (payPeriod.contains(date)) {
amount.add(card.getHours() * payRate);
}
}
payDispatcher.pay(this, date, amount);
}
...
}
public class Employee {
private void dispatchPayment() {
Money amount = new Money();
for (Iterator it = timecards.iterator(); it.hasNext(); ) {
Timecard card = (Timecard) it.next();
if (payPeriod.contains(date)) {
amount.add(card.getHours() * payRate);
}
}
payDispatcher.pay(this, date, amount);
}

public void pay() {
logPayment();
dispatchPayment();
}

private void logPayment() {
...
}
}
public class Employee {
public void makeLoggedPayment() {
logPayment();
pay();
}

public void pay() {
...
}

private void logPayment() {
...
}
}
  • Wrap method is better as they don’t increase the size of existing methods.
  • Wrap method explicitly makes the new functionality independent of existing functionality.
  • The primary disadvantage of Wrap Method is that it can lead to poor names.
class Employee {
public void pay() {
Money amount = new Money();
for (Iterator it = timecards.iterator(); it.hasNext(); ) {
Timecard card = (Timecard) it.next();
if (payPeriod.contains(date)) {
amount.add(card.getHours() * payRate);
}
}
payDispatcher.pay(this, date, amount);
}
...
}
class LoggingEmployee extends Employee {
public LoggingEmployee(Employee e) {
employee = e;
}

public void pay() {
logPayment();
employee.pay();
}

private void logPayment() {
...
}
...
}
  • Implementing Wrap Class might be hard to chew upon at the beginning since large code bases are pretty hard to work.

TLDR; These are few techniques that we can use to make changes without getting exisiting classes under test, but beaware there are pros and cons to these techniques.

Previous Part I: Chapter 5:: Tools

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store