When A Method Can Do Nothing
Refactoring is full of choices. When I start to work on a large method I look at its overall structure and try to get a sense of how to break it down. Conditional statements are often problematic for me.
if (...) {
...
}
When I have an if-statement like this I know that I have a choice. I can extract the if and its associated block into a new method, or I can just extract the contents of the block. I don't think that one way or the other is always right - it comes down to context. Sometimes the condition is important enough to highlight - at other times it seems that the if and its block can be nicely extracted, leaving the calling context nice and tidy. But, what do we call the method we extract?
Let's look at an example with actual names:
if (alarmEnabled) {
Alarm alarm = new Alarm();
...
...
alarm.sound();
}
We could take that entire if condition and its block and extract them to a method named soundAlarm.
soundAlarm();
Is this good?
Most people's initial reaction is to say that the code is lying to us now. It tells us that we are going to sound an alarm, but sometimes we won't. In general, it's bad to have lies in code, so it's better to opt for the other strategy:
if (alarmEnabled) {
soundAlarm();
}
Except, this doesn't seem as clean as it could be.
This dilemma reminds me a bit of the Null Object Pattern. Null Object is another case where people often have the sense that the code may be lying to them. In the calling context, they see a message send to an object and they are shocked when they discover that the action didn't actually happen. But, not everybody feels that way. I've worked with teams who've used Null Object extensively and it's just part of their understanding of the code that when you tell an object to do X, sometimes it won't - sending the message X can imply that sometimes X does not happen. It's up to the object.
My gut tells me that this is okay, and that getting to this understanding is important - it's an issue of deep perception.
If polymorphism means anything at all, it means that the object is in charge. We send it a message and it is up to it to decide what to do. That's core to OO and part of Alan Kay's original view of objects - that they are all about messaging. That said, it is not the dominant view today.
When I really want to make these sorts of extractions clear I usually use one of two strategies - I use a name that implies an event or I generalize the operation. In this case, I'll opt for an event-ish name by using past tense.
intruderDetected();
The nice thing about event-style naming is that the emphasis is on the condition rather than the action. We're now free to put anything we like into our method intruderDetected.
We could, as well, opt to generalize the operation. Instead of saying that we are sounding an alarm, we can say that we are notifying the world about things relevant to our state.
performNotifications();
If I use a name like performNotifications I'm raising the level of abstraction. The fact that I am performing only one notification doesn't matter. The key thing is that the calling context triggers the action but doesn't say anything that could be interpreted as a lie.
I wonder, though, whether we will ever get past the implication that a method always does what its name implies?
When it doesn't, do we have to see it as a lie?