0% found this document useful (0 votes)
3 views8 pages

Clean Code Part 8

Uploaded by

fperezdhharbor
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
3 views8 pages

Clean Code Part 8

Uploaded by

fperezdhharbor
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd

26 Chapter 2: Meaningful Names

Don’t Be Cute
If names are too clever, they will be
memorable only to people who share the
author’s sense of humor, and only as long
as these people remember the joke. Will
they know what the function named
HolyHandGrenade is supposed to do? Sure,
it’s cute, but maybe in this case
DeleteItems might be a better name.
Choose clarity over entertainment value.
Cuteness in code often appears in the form of colloquialisms or slang. For example,
don’t use the name whack() to mean kill(). Don’t tell little culture-dependent jokes like
eatMyShorts() to mean abort().
Say what you mean. Mean what you say.

Pick One Word per Concept


Pick one word for one abstract concept and stick with it. For instance, it’s confusing to
have fetch, retrieve, and get as equivalent methods of different classes. How do you
remember which method name goes with which class? Sadly, you often have to remember
which company, group, or individual wrote the library or class in order to remember which
term was used. Otherwise, you spend an awful lot of time browsing through headers and
previous code samples.
Modern editing environments like Eclipse and IntelliJ-provide context-sensitive clues,
such as the list of methods you can call on a given object. But note that the list doesn’t usu-
ally give you the comments you wrote around your function names and parameter lists.
You are lucky if it gives the parameter names from function declarations. The function
names have to stand alone, and they have to be consistent in order for you to pick the cor-
rect method without any additional exploration.
Likewise, it’s confusing to have a controller and a manager and a driver in the same
code base. What is the essential difference between a DeviceManager and a Protocol-
Controller? Why are both not controllers or both not managers? Are they both Drivers
really? The name leads you to expect two objects that have very different type as well as
having different classes.
A consistent lexicon is a great boon to the programmers who must use your code.

Don’t Pun
Avoid using the same word for two purposes. Using the same term for two different ideas
is essentially a pun.
Add Meaningful Context 27

If you follow the “one word per concept” rule, you could end up with many classes
that have, for example, an add method. As long as the parameter lists and return values of
the various add methods are semantically equivalent, all is well.
However one might decide to use the word add for “consistency” when he or she is not
in fact adding in the same sense. Let’s say we have many classes where add will create a
new value by adding or concatenating two existing values. Now let’s say we are writing a
new class that has a method that puts its single parameter into a collection. Should we call
this method add? It might seem consistent because we have so many other add methods,
but in this case, the semantics are different, so we should use a name like insert or append
instead. To call the new method add would be a pun.
Our goal, as authors, is to make our code as easy as possible to understand. We want
our code to be a quick skim, not an intense study. We want to use the popular paperback
model whereby the author is responsible for making himself clear and not the academic
model where it is the scholar’s job to dig the meaning out of the paper.

Use Solution Domain Names


Remember that the people who read your code will be programmers. So go ahead and use
computer science (CS) terms, algorithm names, pattern names, math terms, and so forth. It
is not wise to draw every name from the problem domain because we don’t want our
coworkers to have to run back and forth to the customer asking what every name means
when they already know the concept by a different name.
The name AccountVisitor means a great deal to a programmer who is familiar with
the VISITOR pattern. What programmer would not know what a JobQueue was? There are
lots of very technical things that programmers have to do. Choosing technical names for
those things is usually the most appropriate course.

Use Problem Domain Names


When there is no “programmer-eese” for what you’re doing, use the name from the prob-
lem domain. At least the programmer who maintains your code can ask a domain expert
what it means.
Separating solution and problem domain concepts is part of the job of a good pro-
grammer and designer. The code that has more to do with problem domain concepts
should have names drawn from the problem domain.

Add Meaningful Context


There are a few names which are meaningful in and of themselves—most are not. Instead,
you need to place names in context for your reader by enclosing them in well-named
classes, functions, or namespaces. When all else fails, then prefixing the name may be nec-
essary as a last resort.
28 Chapter 2: Meaningful Names

Imagine that you have variables named firstName, lastName, street, houseNumber, city,
state, and zipcode. Taken together it’s pretty clear that they form an address. But what if
you just saw the state variable being used alone in a method? Would you automatically
infer that it was part of an address?
You can add context by using prefixes: addrFirstName, addrLastName, addrState, and so
on. At least readers will understand that these variables are part of a larger structure. Of
course, a better solution is to create a class named Address. Then, even the compiler knows
that the variables belong to a bigger concept.
Consider the method in Listing 2-1. Do the variables need a more meaningful con-
text? The function name provides only part of the context; the algorithm provides the rest.
Once you read through the function, you see that the three variables, number, verb, and
pluralModifier, are part of the “guess statistics” message. Unfortunately, the context must
be inferred. When you first look at the method, the meanings of the variables are opaque.

Listing 2-1
Variables with unclear context.
private void printGuessStatistics(char candidate, int count) {
String number;
String verb;
String pluralModifier;
if (count == 0) {
number = "no";
verb = "are";
pluralModifier = "s";
} else if (count == 1) {
number = "1";
verb = "is";
pluralModifier = "";
} else {
number = [Link](count);
verb = "are";
pluralModifier = "s";
}
String guessMessage = [Link](
"There %s %s %s%s", verb, number, candidate, pluralModifier
);
print(guessMessage);
}

The function is a bit too long and the variables are used throughout. To split the func-
tion into smaller pieces we need to create a GuessStatisticsMessage class and make the
three variables fields of this class. This provides a clear context for the three variables. They
are definitively part of the GuessStatisticsMessage. The improvement of context also allows
the algorithm to be made much cleaner by breaking it into many smaller functions. (See
Listing 2-2.)
Don’t Add Gratuitous Context 29

Listing 2-2
Variables have a context.
public class GuessStatisticsMessage {
private String number;
private String verb;
private String pluralModifier;
public String make(char candidate, int count) {
createPluralDependentMessageParts(count);
return [Link](
"There %s %s %s%s",
verb, number, candidate, pluralModifier );
}
private void createPluralDependentMessageParts(int count) {
if (count == 0) {
thereAreNoLetters();
} else if (count == 1) {
thereIsOneLetter();
} else {
thereAreManyLetters(count);
}
}
private void thereAreManyLetters(int count) {
number = [Link](count);
verb = "are";
pluralModifier = "s";
}
private void thereIsOneLetter() {
number = "1";
verb = "is";
pluralModifier = "";
}
private void thereAreNoLetters() {
number = "no";
verb = "are";
pluralModifier = "s";
}
}

Don’t Add Gratuitous Context


In an imaginary application called “Gas Station Deluxe,” it is a bad idea to prefix every
class with GSD. Frankly, you are working against your tools. You type G and press the com-
pletion key and are rewarded with a mile-long list of every class in the system. Is that
wise? Why make it hard for the IDE to help you?
Likewise, say you invented a MailingAddress class in GSD’s accounting module, and
you named it GSDAccountAddress. Later, you need a mailing address for your customer con-
tact application. Do you use GSDAccountAddress? Does it sound like the right name? Ten of
17 characters are redundant or irrelevant.
30 Chapter 2: Meaningful Names

Shorter names are generally better than longer ones, so long as they are clear. Add no
more context to a name than is necessary.
The names accountAddress and customerAddress are fine names for instances of the
class Address but could be poor names for classes. Address is a fine name for a class. If I
need to differentiate between MAC addresses, port addresses, and Web addresses, I might
consider PostalAddress, MAC, and URI. The resulting names are more precise, which is the
point of all naming.

Final Words
The hardest thing about choosing good names is that it requires good descriptive skills and
a shared cultural background. This is a teaching issue rather than a technical, business, or
management issue. As a result many people in this field don’t learn to do it very well.
People are also afraid of renaming things for fear that some other developers will
object. We do not share that fear and find that we are actually grateful when names change
(for the better). Most of the time we don’t really memorize the names of classes and meth-
ods. We use the modern tools to deal with details like that so we can focus on whether the
code reads like paragraphs and sentences, or at least like tables and data structure (a sen-
tence isn’t always the best way to display data). You will probably end up surprising some-
one when you rename, just like you might with any other code improvement. Don’t let it
stop you in your tracks.
Follow some of these rules and see whether you don’t improve the readability of your
code. If you are maintaining someone else’s code, use refactoring tools to help resolve these
problems. It will pay off in the short term and continue to pay in the long run.
3
Functions

In the early days of programming we composed our systems of routines and subroutines.
Then, in the era of Fortran and PL/1 we composed our systems of programs, subprograms,
and functions. Nowadays only the function survives from those early days. Functions are
the first line of organization in any program. Writing them well is the topic of this chapter.

31
32 Chapter 3: Functions

Consider the code in Listing 3-1. It’s hard to find a long function in FitNesse,1 but
after a bit of searching I came across this one. Not only is it long, but it’s got duplicated
code, lots of odd strings, and many strange and inobvious data types and APIs. See how
much sense you can make of it in the next three minutes.

Listing 3-1
[Link] (FitNesse 20070619)
public static String testableHtml(
PageData pageData,
boolean includeSuiteSetup
) throws Exception {
WikiPage wikiPage = [Link]();
StringBuffer buffer = new StringBuffer();
if ([Link]("Test")) {
if (includeSuiteSetup) {
WikiPage suiteSetup =
[Link](
SuiteResponder.SUITE_SETUP_NAME, wikiPage
);
if (suiteSetup != null) {
WikiPagePath pagePath =
[Link]().getFullPath(suiteSetup);
String pagePathName = [Link](pagePath);
[Link]("!include -setup .")
.append(pagePathName)
.append("\n");
}
}
WikiPage setup =
[Link]("SetUp", wikiPage);
if (setup != null) {
WikiPagePath setupPath =
[Link]().getFullPath(setup);
String setupPathName = [Link](setupPath);
[Link]("!include -setup .")
.append(setupPathName)
.append("\n");
}
}
[Link]([Link]());
if ([Link]("Test")) {
WikiPage teardown =
[Link]("TearDown", wikiPage);
if (teardown != null) {
WikiPagePath tearDownPath =
[Link]().getFullPath(teardown);
String tearDownPathName = [Link](tearDownPath);
[Link]("\n")
.append("!include -teardown .")
.append(tearDownPathName)
.append("\n");
}

1. An open-source testing tool. www.fi[Link]


Functions 33

Listing 3-1 (continued)


[Link] (FitNesse 20070619)
if (includeSuiteSetup) {
WikiPage suiteTeardown =
[Link](
SuiteResponder.SUITE_TEARDOWN_NAME,
wikiPage
);
if (suiteTeardown != null) {
WikiPagePath pagePath =
[Link]().getFullPath (suiteTeardown);
String pagePathName = [Link](pagePath);
[Link]("!include -teardown .")
.append(pagePathName)
.append("\n");
}
}
}
[Link]([Link]());
return [Link]();
}

Do you understand the function after three minutes of study? Probably not. There’s
too much going on in there at too many different levels of abstraction. There are strange
strings and odd function calls mixed in with doubly nested if statements controlled by
flags.
However, with just a few simple method extractions, some renaming, and a little
restructuring, I was able to capture the intent of the function in the nine lines of Listing 3-2.
See whether you can understand that in the next 3 minutes.

Listing 3-2
[Link] (refactored)
public static String renderPageWithSetupsAndTeardowns(
PageData pageData, boolean isSuite
) throws Exception {
boolean isTestPage = [Link]("Test");
if (isTestPage) {
WikiPage testPage = [Link]();
StringBuffer newPageContent = new StringBuffer();
includeSetupPages(testPage, newPageContent, isSuite);
[Link]([Link]());
includeTeardownPages(testPage, newPageContent, isSuite);
[Link]([Link]());
}
return [Link]();
}

You might also like