My Hovercraft Is Full of Eels
A story from my Clean Code class. The class exercises revolves around different aspects of the game Tic-Tac-Toe.
I like this game as a platform for exercises: It doesn’t seem to need any requirement specification.
People know the game, and assume that they know exactly how they need to code it. Most of them skip the design phase. It illustrates, every single time, how developers just rush out to “get the job done”, rather than think, design and then code.
One of the first exercises is to start writing code for the game, mostly focusing on names and operations, rather than on actually making it work.
At the end of the exercise we review the code of one of the students. It looked something like this:
public class Game{ public Game() { Board board = new board(); Player playerX = new Player(); Player playerO = new Player(); } ... }
Looks innocent enough, right? I’ve done the exercise more than I can remember, and many times a Game class starts out just like this.
Here’s the thing: Notice that the Game creates the Board? That means there’s a new board for every game.
You know, in real life, there’ll be multiple games on the same board. If we actually thought about modeling the relationship between the Game instances and the Board instances, it would be many-to-few (or even many-to-one, if we’re reusing the board).
Next, the Game creates the Player instances. Since there’s no indication in the requirements who manages the players, the Game is an viable design choice. I’m not going to discuss where it is a good or bad one, but it is a viable one.
Still, as we can read the code, we see that not only does the Game class contains the Player and Board entities, it actually creates them.
Now, would you describe a game like that to a friend who doesn’t know what’s that everyone’s playing?
No. You’d probably describe it like this:
A game is played by two players on a board.
Although the code reads like:
A game creates a board. It then creates the players.
Mincing Words
One of the things I talk about (repeatedly) in the Clean Code class, is the importance of ubiquitous language. That means we use as much as we can names and operation terms from the real domain (the product language) across the requirements, design, code, tests, documentation and operations.
Since we’re dealing with code, what you already see is a dissonance. We see that the relationship between the objects in the model is not as we describe it in the real world.
Of course, it can be a lot more basic. Many attendees who do the exercise describe the Board in the Game like this:
public class Game { boolean [][] multi = new int[3][3]; ... }
I don’t know about you, but never have I heard the game described using the word “array”. Even by developers. Go ahead, ask them.
Mind Your Language
Of course English and Java (or whatever persuasion you come from on either) are different. Languages have differences inherently.
However, what happens when we have translations errors? Sometimes, we understand, no harm done. Sometimes we end up with a weird Hungarian to English dictionary.
In software, we end up with bugs. Some of them are inconsequential. Some of them are built on the wrong assumptions and backed up by the wrong translation.
How do we fight the translation errors? Well, first acknowledge that programming is a translation activity, and as such prone to translation errors.
Second, don’t skip the design. Use the terms from the domain in the code.
But also, tell the usage story. If we describe the game like this:
A game is played by two players on a board.
Then maybe the code should look like this:
public class Player { public void play(Game game) {...} } public class Game public void setBoard(Board board) {...} }
There are only a few that read as the game is described. If you want easily maintainable design (and who doesn’t) start with as less translation as possible.
You’ll thank yourself later.
Published on Java Code Geeks with permission by Gil Zilberfeld, partner at our JCG program. See the original article here: My Hovercraft Is Full of Eels Opinions expressed by Java Code Geeks contributors are their own. |