The instructors wish, when they were first learning test-driving, refactoring, and deep OO, that they had had a genuine side-by-side comparison between code Heaven and code Hell. Such an object lesson would have made the value and benefits of agile programming practices so much more plain, so much sooner. Alas for us, but hurray for you! In this clinic session you will be able to compare and work with two very different implementations of the same problem domain: one of them fabulously ugly, and the other of them — well — much closer to gorgeous.
We’ll provide attendees with a unique opportunity to experience a worst-case and best-case implementation of a 10 x 10 Tic Tac Toe game.
In this very hands-on tutorial, programmers, working in pairs, will be asked to make an innocent-looking requirements tweak in Code Base A, the downright ornery one that is a single 1000-line “class” with a non-trivial cyclomatic complexity, no-unit tests, rampant duplication, muddled flow of control, a great wash of globals, and no real hope. It’s chock full of methods that look like this:
public int someWierdMethodName(int playerMark, int x, int type)
{
int j, k, l;
int position = 0, position2 = 0;
for (l = 0; l < 6; l++) {
for (j = 0; j < SQUARES_PER_SIDE; j++) /* horiz & vert */
{
resetAllMarksAlongAxesForFirstHalfOfBoard();
position = checkFor5AlongHorizAxis(playerMark, x, j, l, position);
if (marksByAxisByPlayerForChecking[0] == 3 && marksByAxisByPlayerForChecking[1] == 2) {
if (type == SETFLAGS_MODE) {
tempTableForChecks[tempRowForChecks[0]] = OCCUPIED;
tempTableForChecks[tempRowForChecks[1]] = OCCUPIED;
}
if (type == CLEAN_MODE) return tempRowForChecks[0];
}
if (marksByAxisByPlayerForChecking[0] == 4 && marksByAxisByPlayerForChecking[1] == 1 && type == CHECK_MODE)
return position;
position = checkFor5AlongVertAxis(playerMark, x, j, l, position);
if (marksByAxisByPlayerForChecking[2] == 3 && marksByAxisByPlayerForChecking[3] == 2) {
if (type == SETFLAGS_MODE) {
tempTableForChecks[tempRowForChecks[0]] = OCCUPIED;
tempTableForChecks[tempRowForChecks[1]] = OCCUPIED;
}
if (type == CLEAN_MODE)
return tempRowForChecks[0];
}
if (marksByAxisByPlayerForChecking[2] == 4 && marksByAxisByPlayerForChecking[3] == 1 && type == CHECK_MODE)
return position;
}
for (j = 0; j < 6; j++) {
resetAllMarksAlongAxesForFirstHalfOfBoard();
for (k = 0; k < 5; k++)
{
position = checkFor5AlongDiagDownRightAxis(playerMark, x, j, k, l, position);
position2 = checkFor5AlongDiagUpRightAxis(playerMark, x, j, k, l, position2);
}
if (marksByAxisByPlayerForChecking[0] == 3 && marksByAxisByPlayerForChecking[1] == 2) {
if (type == SETFLAGS_MODE) {
tempTableForChecks[tempRowForChecks[0]] = OCCUPIED;
tempTableForChecks[tempRowForChecks[1]] = OCCUPIED;
}
if (type == CLEAN_MODE) return tempRowForChecks[0];
}
if (marksByAxisByPlayerForChecking[0] == 4 && marksByAxisByPlayerForChecking[1] == 1 && type == CHECK_MODE) return position;
if (marksByAxisByPlayerForChecking[2] == 3 && marksByAxisByPlayerForChecking[3] == 2) {
if (type == SETFLAGS_MODE) {
tempTableForChecks[tempRowForChecks[0]] = OCCUPIED;
tempTableForChecks[tempRowForChecks[1]] = OCCUPIED;
}
if (type == CLEAN_MODE) return tempRowForChecks[0];
}
if (marksByAxisByPlayerForChecking[2] == 4 && marksByAxisByPlayerForChecking[3] == 1 && type == CHECK_MODE) return position2;
}
}
return (NONE);
}
So, you know what? That method was even scarier before we got a hold of it. Almost none of the variable names revealed any intention, and all of those semi-helpful-sounding utility methods? They were all in-line. So it was actually worse than this. But there are limits to our cruelty, it turns out. We had to fix it up at least a little
To get a full sense of how scary this codebase is, please see the attached Crap4J report that shows the “percentage of crappy methods” metric pegged at 20.88 out of 15. (Crap4J is a great little one-metric tool that measures how well test-covered the worst of your code is.)
Be afraid. Be very afraid. We predict that NO-ONE will be able to demonstrate that they have made this seemingly simple requirements tweak in the time allotted.
Attendees will then be asked to test-drive the same requirements tweak in Code Base B, the friendly one with the requisite object model that is well test-driven, well test-protected, and well factored.
Both of these Tic-Tac-Toe games are playable as Java applets. And you know what? They’re good! They beat the instructors most of the time (which, admittedly, might not be saying all that much).
Many will be astonished at how unspeakably different from each other these codebases are. Many test-driving and refactoring novices will be flabbergasted at the difference in these two systems’ extensibility and clarity.
This is not a complete survey of all of the differences between old-fashioned bad procedural code, and new-fangled agile OO goodness. It is instead a deep dive, from the perspective of a poor programmer with a nasty deadline.
Note: As part of the Clean Code Clinic, we’ll hold a 90-minute session twice. You can attend either one.
We’ll get everyone set up with the Ugly code base in Eclipse or Idea, as they prefer, as quickly as we can. We want to get to coding with very little introductory preamble.
All programming will be in pairs or small groups.
Duration: 15 minutes.
We’ll then subject everyone to the seemingly innocent little requirements change in the Ugly code base. We’ll enforce the deadline ruthlessly. There will be wailing and gnashing of teeth.
Duration: 30 minutes.
We’ll next have a retrospective on the first exercise, with brief synopses from different teams. We’ll all share the pain. We’ll then have a summary of agile/XP programmer practices that can prevent this kind of heartache entirely.
Duration: 15 minutes
Next, everyone will be given Friendly, Nice Code Base B (again, with a clear, simple model, exhaustive unit tests, etc). They’ll be given a bit of time to attempt the same requirements tweak. It will take astonishingly little time for the OO cats in the room.
Duration: 15 minutes.
We’ll retrospect: (Whoa, what just happened? How in the world can Code Base A be so completely intractable for this requirement compared to Code Base B?)
We’ll celebrate all the Aha! moments. We’ll all take one last deep, relaxing breath.
We’ll close with Q & A.
Duration: 15 minutes.