Converting Queries to Commands
A `Slice and Dice' Refactoring
When methods focus on a single responsibility, they fall into two broad categories. They are either queries that compute and return some value without producing side effects, or they are commands that change the state of the world or the state of the object they are on.
The former style is more common in functional programming. In fact, we can say that functional programming is all about organizing our code around pure queries. In contrast, OO biases toward methods that return void. We call them to send a message to an object, telling it something. The object can then change its own state or state someplace else in the world.
When refactoring, it can be useful at times to change from one style to the other. One particular case is when code queries an object only to go back to the same object and do things to it. Here’s an example:
if (customer != null && customer.isActive()) {
if (notice.isQuarterly()) {
customer.clearQuarterlyNotices();
}
customer.addNotice(notice);
}
Here we have a bit of work that happens conditionally based upon a query to Customer. If the state of the object is active we can do all of it.
When I look at code like this and consider how to refactor it, I often think about whether I should extract the code of the if-statement into a new method or extract the entire if-statement. I know that the work belongs on the Customer class but how do we deal with the conditionality of the code? I think we can get an answer by going back to the basics of object orientation.
OO’s primary advantage is decoupling. We send messages to objects and it is up to them to decide what to do. This view of OO comes from Alan Kay and it takes quite a while to internalize. One of the things you have to accept is that when you tell an object to do something there’s no guarantee that it will actually do it. You could, for instance, tell a graphical widget to move but it may not. It could be a null object that simply receives messages and does nothing. These objects can be very useful in systems but you have to maintain the mental frame: what is done depends upon the object. The method calls we make communicate intent but the object bears the responsibility.
In the code above, we have some work that can happen in a customer. We’d like to move it there, but first, let’s imagine what it would look like if we were telling something to do the work rather than asking whether it should be done.
customer.whenActive((c) -> {
if (notice.isQuarterly()) {
c.clearQuarterlyNotices();
}
c.addNotice(notice);
});
This code looks a lot like our original code. The difference is that we’ve replaced our query about the state of Customer with a method that executes a lambda on the customer object (this) when it is active.
In this context we’re not asking, we’re telling. The lambda is pretty much exactly the body of the if-statement we had previously.
We don’t have to go this far, though. I was just illustrating the how the block of an if-statement with a conditional made of queries can be seen as a command. Let’s extract the original if-statement and move it onto Customer:
class Customer {
…
public void acceptNotice(Notice notice) {
if(isActive()) {
if (notice.isQuarterly()) {
clearQuarterlyNotices();
}
addNotice(notice);
}
}
}
How do you feel about the fact that this method may not add a notice? What we have to think about is whether the name acceptNotice is likely to be construed as indication that the customer will hold onto it. For the most part, the names we use can help communicate the degree of decoupling we want. Function names in procedural code often describe the work that they do. The possibility of polymorphism in OO lets us be more abstract. We can name them after our intentions and allow that objects may do the work whatever way they wish. In this case, I’m using the name acceptNotice, which is about as noncommittal as you can get with regard to the work that the method will be doing. In a way it’s like giving an object some data and saying “your turn!”
If I wanted to generalize a bit more I’d use a name that hints at event-iness, like onNewNotice.
class Customer {
…
public void onNewNotice(Notice notice) {
if(isActive()) {
if (notice.isQuarterly()) {
clearQuarterlyNotices();
}
addNotice(notice);
}
}
}
When we move from queries to commands, we often have to raise the abstraction level of names. It’s fine to do. For me, it aligns with my philosophy that objects are for decoupling.