Revisiting Nouns and Verbs


There Is No Spoon

Something learned early when studying OOP is to name classes as nouns or noun-phrases and name methods as verbs. It seems like too simple an idea to make better object-oriented designs, but it makes a big difference. In fact, there is reasonable evidence that adherence to this idea could make a design, and ignorance of it could break one. The evidence comes from my own experiences refactoring code as well as what I’ve recently learned while studying Smalltalk.

Naming has such an effect because better names reduce the context an object has, i.e., what an object is aware of versus what it needs awareness of to do its job. Removing unnecessary information raises the level of abstraction providing more opportunity for reuse and minimizes the chance that the message needs changing when requirements change.

A message name constrains the possible interpretations to some extent, which is good. This indicates what the program is doing when the object sends the message and hints to implementing objects the context in which they are operating. However, we should only constrain the meaning of a message as much as necessary and not so much to exclude other valid uses.

To summarize what I’ll be talking about: If an object sends a message that mentions any noun other than those that indicate the parameters it’s passing then there is likely added context and a case for renaming.

Notice I said “likely”. There’s no need to deal in absolutes (as only the Sith do). As I’ve learned more about programming, and life, I’ve learned that nothing is absolute. Everything is relative. You can find exceptions to every rule. Black and white are just acute versions gray.

Messages should only include nouns within their context because messages are ignorant of the things that respond to them and the inner parts of the things that respond to them.

If you remember the movie “The Matrix”*, there is the famous scene when Neo visits The Oracle and meets a boy who bends spoons with his mind. When Neo attempts this, the boy gives him a helpful tip:

Do not try and bend the spoon, that’s impossible. Instead, only try to realize the truth…there is no spoon. Then you will see it is not the spoon that bends, it is only yourself. -Spoon Boy, The Matrix movie

The meaning behind this is:

The spoon exists only in the Matrix, which really means it doesn’t exist. It’s a lesson for Neo, to help him realize that manipulating the Matrix isn’t about focusing on an object and trying to change it. The object doesn’t exist, so he can’t change it, he has to change himself. –http://thematrix101.com/matrix/faq.php

In OOP, an object should focus on itself using messages and not worry about other objects. From the object’s perspective, there are no other objects.

Your Favorite Example

Let’s use everyone’s favorite example of a car. You know you love a good car example. The only reason I don’t like this example is because it’s not reflective of any real world case. For the same reason, I think this example is good because there’s no need to get lost in understanding a less common domain, or in the details of implementation.

What’s wrong with this interface?


public interface Car {

    void startEngine();
}

I guess what’s wrong depends on the situation (remember the gray area). Some object needs to start the car. We create a message to start the engine because we (the humans; the programmers) know cars have engines. What is this engine thing? In the context of cars, Google (where else?) defines an engine as “a machine with moving parts that converts power into motion.”

We realize while writing the class for our favorite imaginary electric car from this awesome example that electric cars don’t have engines. They have motors. Contextually, it doesn’t make sense for the ElectricCar class to implement the Car interface. Not a problem. Make a new interface.


public interface ElectricCar {

    void startMotor();
}

Now let’s implement a class that can start any kind of car, maybe a mechanic at a shop:


public interface StartsCars {

    void start(final Car car);

    void start(final ElectricCar electricCar);
}

public class Mechanic implements StartsCars {
    
    @Override
    public void start(final Car car) {
        car.startEngine();
    }

    @Override
    public void start(final ElectricCar electricCar) {
        electricCar.startMotor();
    }
}

Hidden details of car classes (the type of car; how it’s built) force objects that want to start cars to change with them. The calling object must know which car type it’s starting to call the correct method. This is the sort of code that when adding new car classes leads to adding methods to the StartsCars interface, switching on types, or adding properties to inspect, like isElectric(). All of which we don’t want.

It Means… You Know… Whatever

We might take the stance that an engine abstracts “whatever it is that puts the car into motion” and startEngine() is a sufficient abstraction for all types of cars. We could say that. But that little word “engine” leaves it open to interpretation. If engine in this context is synonymous with “whatever motivates the car”, then why mention it at all?

We’re injecting context from our human perspective. We should write the message based on the calling object’s perspective. It’s sometimes difficult to get around this. We are human (gray area?) and in our world we encounter implementations of things, not abstractions.

The problems with including the word “engine” are:

  • It’s adding context that’s open to interpretation. It’s a word with a specific meaning, but is also interpreted more broadly by different people.
  • It’s forcing you to think about an implementation detail that is otherwise hidden from the caller, so someone might be compelled to make other interfaces (like we just did) that focus on other implementation details. Engines, motors, compressed air, jet turbines, etc. None of them are important here.

Here’s what I think is a better solution:


public interface Car {

    void start();
}


public class RegularCar {
    
    private final Engine engine;

    public RegularCar(final Engine engine) {
        this.engine = engine;
    }

    void start() {
        // engine.whatever...
    }
}


public class ElectricCar {
    
    private final Motor motor;

    public (final Motor motor) {
        this.motor= motor;
    }

    void start() {
        // motor.whatever...
    }
}

You might think this is obvious. Maybe it’s obvious in this domain, because we’re familiar. In less familiar domains, it’s hard to realize that many nouns in method names are unnecessary or too specific.

Notice the start method is a verb. Adding a noun to it should require adding a parameter. It would depend on what we are building, but we still may not want to use the word engine; which brings me to another point.

I’ve found it helpful when mentioning nouns in message names to generalize the noun to something that is intrinsic to all sub classes. For example, while all cars don’t have engines, they all have something that provides power.

If we did decide to parameterize this, we might use something like a strategy object that represents a more general concept of things that provide power:


public interface Car {

    void startPowerProvider(final PowerProvider powerProvider);
}

// Or we can use a preposition with a verb
// I'll go into more detail about this later
public interface Car {

    void startWith(final PowerProvider powerProvider);
}

This might seem like an odd message to send to a car, but remember we’re not modeling the real world, only resembling it enough to feel familiar. Also, don’t blame me if this doesn’t make sense. It’s your favorite example, not mine.

If a message is talking about the “thing that provides power”, then the caller should pass it as part of the message.

If you’re wondering why not generalize the engine to use something like PowerProvider, such as:


public interface Car {

    void startPowerProvider();
}

It’s because this message, without a parameter, implies the caller knows about something hidden inside the receiver (an implementation detail), while the parameterized one implies the caller provides something that is a configurable part of the message and relevant in the calling context. What if the message to the car was startTeaPot() or startSpaghetti()? Why are we talking about something that is not present in the current context? Don’t worry though, startWith(PowerProvider) still permits implementing a teapot or spaghetti powered car by creating classes that implement the PowerProvider interface.

What’s the Deal with Airline Food Nouns?

Nouns are implementation details. Think about it. When you ask someone how something works, they typically name all the parts (the nouns). Here’s a good example: How Stuff Works: Manual Transmissions. Watch just the first 30 seconds of the video on this page about how manual transmissions work and listen to all the nouns mentioned. You could probably lookup any video on the subject and find the same thing. Those nouns don’t matter when they put the casing on the transmission and it becomes a black box. Even to the internal parts the only thing that matters are the interactions between them.

Getting back to code. Consider getter methods. They are very noun-like: “get me a thing”, they say.

C# has an evil feature called properties, which remove the inane get verb and just let you use a noun as the message name.

Aside from lacking a bidirectional relationship between data and behavior, these are particularly bad cases of context. Getters not only include nouns in the message, they ask that noun be physically present and standing at attention for the programmer to use. Widespread use of getters goes further to drag entire object graphs into the calling context. Once you have a thing, you can say “get me another thing” from that object, and so on until we visit every node in the object graph and scatter every detail of the system everywhere. So much for local retention.

One problem with getters (and setters) is when we are implementing a class that behaves similarly to others (implements the same interface), but isn’t implemented with that noun internally. For some instances the property exists and for others it doesn’t. The typical “fix” is null checks… everywhere.

I assert another problem, that getters and setters are forms of duplication. The code gods say “Don’t Repeat Yourself (DRY)”. We try very hard not to repeat ourselves, but often draw the line at getters (…and setters).

How is this duplication? We store the information in one place (a class), and that class is where we access it. No duplication; there’s only one place to access it, right? Yes, actually that is right. But that’s not the duplication part. The duplication comes when we call the getter/setter. Every time we do, we repeat “this information is stored here”, over and over again. It’s duplication of… location? Here’s an example:


public class A {

    private X x;

    public X getX() { ... }
}

A stores a reference to an X.

Assuming we have an A instance.


// Somewhere in the code...
a.getX() // Is X stored in A? Yes it is.

// Somewhere else in the code...
a.getX() // Is X stored in A? Yes it is... again.

If the graph is longer, it’s the same problem only orders of magnitude worse.


// Assuming class X contains an instance of class Y, and class Y contains an
// instance of class Z...
// Somewhere in the code...
a.getX().getY().getZ()
// Is X stored in A, is Y stored in X, and is Z stored in Y? Yes it is.

a.getX().getY().getZ() // Is X stored in A, and is... OK that's enough.

The classes define the storage location for a piece of knowledge once, but repeat the path to that knowledge every time we use one or more getters/setters. If that path should happen to change, then so must our duplicates.

Using messages correctly, and techniques like East Oriented code, the information is in one place; inside the class. It doesn’t move. As messages pass through an object they influence the object to act on its stored collaborators and send new messages.

Don’t bind to the “things” that do the work, because the point of OOP is we don’t care what thing does the work. It’s only important that the object perform actions to carry out its job, and we express actions in messages.

Nobody Puts Nouns in the Corner

That’s not to say nouns don’t belong in messages, but they have their place. We can find where nouns belong by looking at the canonical OO example of Smalltalk.

Smalltalk messages are like sentences. They are so sentence-like that expressions end with periods. Introduce parameters and they are like sentences with holes in them where we can insert variable parts. In keyword messages, the keywords define the action. They are usually verbs and sometimes contain adjectives to constrain the message, or prepositions to describe relationships to parameters. In many cases they only use nouns to refer to parameters and not something that is unknown to the calling context. One of the few cases I’ve seen where they do use nouns outside of the calling context is in conversion methods, when an object converts to and returns a new value as a different class.

Lets take a Rectangle class as an example. A Rectangle can expand by a delta. Here it is in Smalltalk:


| rectangle delta |
rectangle := Rectangle origin: (Point x: 1 y: 2) corner: (Point x: 3 y: 4).
delta := Point x: 2 y:2.
rectangle := rectangle expandedBy: delta.

The Java equivalent:


Rectangle rectangle = new Rectangle(new Point(1,2), new Point(3,4));
Point delta = new Point(2,2);
rectangle = rectangle.expandedBy(delta);

On a side note, the Rectangle class produces new versions of itself instead of mutating the object, which is why this returns a new rectangle. This is not a getter. It’s what Barbara Liskov refers to as a producer in her book “Program Development in Java: Abstraction, Specification, and Object-Oriented Design” (see Reoriented). It’s as harmless as returning self, because it’s returning a new modified version of itself. It’s almost like a constructor method on an instance. Classes often use this style to simulate immutability.

The definition for expandedBy: shows another example of message naming that includes a noun.


expandedBy: delta 
    "Answer a Rectangle that is outset from the receiver by delta. delta is a Rectangle, Point, or scalar."

    ^delta expandRectangle: self

It sends the expandRectangle: message to the delta object. The definition for expandRectangle: in the Point class begins like this:


expandRectangle: aRectangle
"The implementation is unimportant..."

The message mentions a noun as part of the selector, but as we can see by the definition it refers to a parameter. It doesn’t add any context.

The Java and Smalltalk versions of “expandedBy” look quite similar, but things change as we add keywords to the message, which doesn’t translate as nicely to C-style languages like Java.

Smalltalk version:


"The message selector here is insetOriginBy:CornerBy: and is being sent to the rectangle"
rectangle := rectangle insetOriginBy: originDeltaPoint cornerBy: cornerDeltaPoint.

The Java version starts looking a little wonky:


rectangle = rectangle.insetOriginByCornerBy(originDeltaPoint, cornerDeltaPoint);

We could easily write this as:


rectangle := rectangle insetBy: originDeltaPoint AndBy: cornerDeltaPoint.

or


rectangle = rectangle.insetByAndBy(originDeltaPoint, cornerDeltaPoint);

and the context doesn’t change because we’re only dealing with the message and not the object’s internals.

A Real Example

Here’s a real example from a recent experiment I’m working on, which is a “toy robot simulator” (github: toyrobotsimulator). The gist of it is to send commands by file or command line that simulate a toy robot moving around a closed grid. In the ToyRobotInputCommandParser class, I had a private method defined as:


private void sendExtractedNameAndParametersTo(final String commandText,
                                              final CommandReceiver commandReceiver) {
    // ...
}

The method’s goal is to extract a command name and parameters from a string containing command text in a predefined format. Mentioning the “name” and “parameters”, which do not appear as part of the message parameters adds unnecessary context. I’m assuming what is happening inside the method.

Had this been a public method of an interface, I’m declaring that someone building a class implementing that interface cannot extract anything else from command text except a name and parameters. Those extra nouns constrain the message beyond what is necessary.

I changed it to use a verb describing what we’re doing, and to only mention the parameters (the preposition, “To”, refers to the CommandReceiver:


private void tokenizeCommandTextTo(final String commandText,
                                   final CommandReceiver commandReceiver) {
    // ...
}

This message indicates the same type of action, but with much less context.

Private methods are candidates for public methods of an interface, so let’s imagine this method is a public method of an interface. This opens the message to more varied implementations and more reuse while still constraining the action to mean what we want accomplished.

Let’s see what these might look like in Smalltalk:


sendExtractedNameAndParametersFrom: commandText To: aCommandReceiver.
    "... "

Now it’s easier to see the added context.

Compare this to:


tokenizeCommandText: commandText ToCommandReceiver: aCommandReceiver.
    "... "

"or"

tokenize: commandText To: aCommandReceiver.
    "... "

Summary and Additional Notes

  1. Name classes as nouns or noun-phrases or in some cases nominalized verbs. The last one happens when your domain requires certain actions with more interesting behavior. For example, a banking application might have Withdrawal and Deposit classes which are nominalized forms of the verbs withdraw and deposit. These represent “things” in space and time (withdrawals and deposits are actions taken on accounts, but also represent an event in time that may have additional information to capture, or messages to send).
  2. Name messages as verbs. Messages can include adjectives to help constrain the message and prepositions help describe relationships to parameters.
  3. Messages can contain nouns, but with care. Nouns add context to messages. Sometimes the context is okay, and sometimes it’s bad. It’s ok when the noun represents and intrinsic property of the object in the domain, and for interfaces an intrinsic property of sub classes. It’s dangerous when the noun represents a detail of a single implementation of the message, like the car.startEngine() example. We elude to the idea that a car has an engine. Does an engine represent and intrinsic property of cars in our domain, or is it an example (a detail of a single implementation) of how cars might start? If our application domain is a Nascar type racing game, then I would say this metaphor is ok. If our application had a wider domain encompassing details of electric cars, then this metaphor doesn’t make sense. Electric cars typically have a motor (or motors), but not an engine. It’s up to the developer and their knowledge of the domain to decide if the context is ok.

* I’m assuming you’ve seen the movie “The Matrix”, unless you’re reading this far in the future and you’re a member of a generation who considers movies from 1999 “old” and “boring”. You’re missing out.

2 thoughts on “Revisiting Nouns and Verbs

  1. Your post is very interesting, but i think we can go one step forward.
    Smalltalk syntax allow developers to write message using sentence.
    So, the syntax can be used to DESCRIBE the context and let the object to react (i.e. having a behavior) accordingly.
    For exemple, instead of define a `start` message in the `Car` interface, it can be more interesting to define a message to describe the fact that a driver want to go somewhere, i.e. `driver: driver wantToGoTo: destination`.
    With the `start` message, the car instance SHOULD start its engine/motor/nuclear reactor or whatever you want: it’s a command, and if the start procedure failed for any reason, an exception should be throwed or the method can return `false`.
    With the `driver: driver wantToGoTo: destination` message, the world of possible is totally open and the car instance can provide feedback to the driver without throwing exception or return a boolean! The car can send the message `car: car needToRun: kilometers` to an engine, a motor, or nothing if the car instance has no engine/motor, or send the message `motorHasMalfunction: malfunction` to the driver instance receive via `driver: driver wantToGoTo: destination`.
    Or the car instance can send a message to M. Spock to translate it directly at the right place!
    If you use Smalltalk syntax to define message which describe the context, absolutely all become possible.
    Sadly, this kind of syntax is a little bit ugly in language with `C` syntax…

    • Hey Hey! Thanks for reading and taking the time to comment. I think what you said points out a bit of a flaw in the way I presented the information. I wrote the Car interface first and then had the Mechanic use it. What I would have done in reality is design a Car interface as part of implementing the Mechanic class and that Car interface would be: “the ‘Car’ as seen from the Mechanic’s perspective”. It likely would have methods that a normal person would not want to use. You’re right that in other contexts merely “starting” the car is not very high level, but if the mechanic needs to simply start the car as part of doing their job, then this interface would suffice for the mechanic.

      I think the interface for the concept of a “car” would vary depending on who is using it, so if a normal person were traveling by car, then I agree with you that methods like “driver: driver wantToGoTo: destination”, would be better because it’s at the highest level of abstraction for the task that the “person” is doing. I love your idea of passing in the driver that can receive notifications from the car instead of throwing exceptions or returning boolean!

      Coding with messages is so much nicer. It’s really unfortunate that we haven’t moved away from the “C syntax”. I’ve been enjoying learning Smalltalk though and plan to build an app of some kind with it.

Comments!

This site uses Akismet to reduce spam. Learn how your comment data is processed.