Musings on Object Oriented Programming: Asking the Right Questions
Here's a thing I don't like: People asking me stuff like Hey, do you have time on Saturday?
while they want to ask Hey, I'm moving. Can you help me haul all my stuff up and down 22 floors on Saturday?
I have no way knowing what they want until I gave an answer (or asked why?
).
Now take a look at this snippet of code:
if user.author? || user.admin?
show_create_blog_post_button()
end
Can you guess what I don't like about it?
If your answer is that the question hides it's intent you just earned yourself a cookie. Go on. I'll wait while you eat.
In all seriousness though, what the code does want to ask is Is this user allowed to create blog posts?
. But what it does ask is: Is this user an author or an admin?
. That is a big difference and makes the lives of the programmers that have to maintain that code a good bit harder.
Unclear Intent
The single most important aspect of OOP (and probably any form of programming) is intent. If code clearly communicates its intent you will have a much easier time understanding what's going on.
In our example we are only able to gather the intent of the question from the consequences of the answer. To illustrate what I mean have a look at this:
if user.author? || user.admin?
show_blog_post_statistics()
end
Still makes sense, right? The question the code asks does not change, while its intent does, though. Obviously now it wants to ask Is this user allowed to see the statistics?
. Since this change of intent is not reflected in the question however, there is no way to derive the intent of the question without understanding the code it guards. Best case: You have to read a few lines before getting back to the if
statement and being able to make a call if it all makes sense. Worst case: you cannot easily figure out what the code inside the if
does and have no way of telling if the whole thing makes sense or not:
if user.purple? && user.fries?
handle_things()
end
Figuring out what's going on here is pretty much impossible without digging into purple?
, fries?
and/or handle_things
.
Not DRY
In case you never heard about the DRY principle, or why its a pretty good idea Wikipedia is a good place to start reading about it.
Let's say there are a few places where a 'create blog post' button should appear depending on whether you are allowed to create new posts or not. Chances are you will have to ask the same question more than once. Chances are the requirements to write a blog post will change. Tomorrow, management decides that it makes no sense for admins to be able to create blog posts. Now you will have to search for all if
statements that guard the display of the buttons and change them one by one. Sure you can automate this step but there is still a chance you will miss one. And in any case it's unnecessary work.
Imagine we had we asked the right question, though:
self.author? || self.admin?
end
end
# templates
if user.can_write_posts?
# show create blog post button
end
This makes it trivial to just change the can_write_posts?
method of User
and be done.
Hard to Test
Keeping stuff simple helps a lot when trying to reason about it. Testing helps a good bit, too. So, naturally tests should be held to the same standards as the code it tests: Keep it simple.
Unit tests are arguably the simplest kind of test. They usually require little setup, involve minimal state and are focused on one thing only. And as such they are the quickest to write and easiest to reason about.
To write a unit test you need a single method. You don't have a single method, though. You have two: author?
and admin?
. So you have to test the combination of both methods, which is by definition an integration test. Since you probably don't want to write a test to see if combining two boolean values (the return values of author?
and admin?
) works correctly, you have to write a test for the whole view. And view tests are usually complicated and a far shot from the simplicity of a unit test.
Good Questions Are Hard to Ask
Coming up with the right question can be tough. Most of the time it only takes a few minutes, and the gain in readability and code quality will pay off for the rest of the application's life cycle. And that means it will make the daily work life of all those who have to work with the code more pleasant. Its not unlikely that you will be among them.