Test Driven Development: Programming by Intention
'Tis but thy name that is my enemy;
...
O, be some other name!
Romeo and Juliet, Act 2 Scene 2 William Shakespeare
This chapter discusses programming by intention, a central idea in XP. It means making your intent clear when you write code.
Have you ever had to work on a piece of code and found that it was hard to understand? Maybe the logic was convoluted, the identifiers meaningless, or worse, misleading. You say there was documentation? Was it readable? Did it make sense? Was it up to date with the code? Are you sure? How do you know?
The main idea of programming by intention is communication, specifically, the communication of our intent to whomever may be reading the code. The goal is to enable them to understand what we had in mind when we wrote the code.
Let's look at what we need to do in order to have our code be as understandable and intent-revealing as possible.
NAMES
By names I mean the identifiers we choose to name the various classes, variables, methods, etc., that we create as we work. We need to choose names that are semantically transparent, that is, they say what they mean and mean what they say.
When I'm talking about choosing names, I like to pull out the quote from Romeo and Juliet that appears at the beginning of this chapter. We can use whatever word/name we like to refer to a thing, but if it does not convey the meaning that we intend then it is an enemy of clarity and may serve to confuse people who read or work on this code in the future. The lesson here is to use names that make sense. . . call it what it is.
My oldest daughter used to have a cute, although somewhat annoying, habit of coming up with her own names for songs and stories that she liked. She would focus on one line from the whole song/story and derive a name from that. Many nights at bedtime she would ask frantically for a new favorite, while we went through a game of Twenty Questions to try and deduce what she was talking about. This is a danger of using non-obvious names for things. Sure, it may make sense to us, but what about everyone else?
There are several patterns that we can use when choosing names; we discuss them in detail next.
-
Use nouns or noun phrases for class names. Name classes for what they represent or what they do:
public class Movie { //... } public class MovieRatingComparator implements Comparator { //... } public class XMLMovieListReader implements MovieListReader { //... }
-
Use either adjectives or generic nouns and noun phrases for interfaces. Interfaces are a bit different. If an adjective is used for an interface name it usually ends with -able, for example, Runnable, Serializable. My advice is to avoid conventions that prepend or append "I" to the name when possible. Sometimes there isn't a good name for an interface other than "interface to something." In that case, ISomething is acceptable.
public interface Serializable { } public interface MovieListWriter { void write(MovieList movieList) throws IOException; }
-
Use verbs and verb phrases for method names. Methods do something, so using verbs to name them makes sense. Examples include add and fetchReview.
private int calculateAverageRating() { int totalRating = calculateTotalRating(); return totalRating / ratings.size(); }
-
Use accepted conventions for accessors and mutators. Many languages have generally accepted conventions for how to retrieve and modify instance variables. For instance, if you are working in Java, you are advised to use the Java Beans conventions of getX and setX to retrieve/modify a variable named x.
public Category getCategory() { return category; } public void setCategory(Category aCategory) { category = aCategory; } public boolean isOfCategory(Category aCategory) { return category.equals(aCategory); }
To access a boolean variable, use isX in both Java and Smalltalk. An alternative in some cases is to use the form isX() where X refers to an optional part of the object; for example, either of the following could be used:
public boolean isRated() { return !ratings.isEmpty(); } public boolean hasRating() { return !ratings.isEmpty(); }
Note that this applies to computed properties as well as actual instance variables, for example, when a circular queue class needs a method to query whether it is full, we suggest isFull.
Sometimes it is clearer to drop the get prefix. For example, I tend to use size rather than getSize to fetch the size of a composite object. This is often the case when the value you are asking for is a property of the object as opposed to one of its attributes.
public int size() { return movies.size(); }
In this case, we are also conforming to the convention used in the Java class libraries. It is a good idea to conform to the naming idioms of the environment you are working in because that is what others who will be reading your code will be accustomed to.
-
Don't put redundant information in method names. If I needed a method to add an instance of X, I would tend to call the method add rather than addX since the parameter will be an X. This has the benefit that if later refactoring changes the type of the argument (even simple renaming of the class), then the method name is out of sync with its argument. The innocent redundancy has now become misleading and confusing. Another benefit is that you gain clarity if you need to support adding of different types by the ability to overload the method name.
public void add(Movie movieToAdd) throws DuplicateMovieException { if (this.contains(movieToAdd)) { throw new DuplicateMovieException(movieToAdd.getName()); } movies.add(movieToAdd); }
There are always exceptions. Sometimes it is just clearer, and reads better, if you have type information in the method name.
public void addRating(Rating ratingToAdd) { ratings.add(ratingToAdd); }
-
Use nouns and noun phrases for variable names. Variables hold things, so nouns make sense, for example, rating, movies, connectionToReviewServer.
public class Movie { private String name = ""; private Category category = Category.UNCATEGORIZED; private List ratings = null; //... }
Choose names carefully, but don't spend too much time at it. Remember, they are easy to change. If later we decide that a different name would communicate the intent better, it can be changed. That's one of the most basic refactorings. Using a tool that has automated refactoring support helps.