Names objects after things, not actions!
The book Object Oriented Programming by Peter Coad and Jill Nicola is a very enjoyable introduction to object thinking. One of the gems it contains is the following:
The “-er-er” principle. Challenge any class name that ends in “-er.” If it has no parts, change the name of the class to what each object is managing. If it has parts, put as much work in the parts that the parts know enough to do themselves.
(For instance: if you have a PersonManager, delete it and move all of its method to class Person.)
Carlo Pescio wrote an excellent blog post on this. Since I discovered this, I found that choosing good names for objects makes it much easier to write good code. It’s much easier to decide where a responsibility belongs.
Now, the team I’m currently coaching with has a Rails project. As a consequence, I’m reading the latest things on object-oriented programming and Ruby and Rails. I read Objects On Rails and from there I discovered Sandi Metz and her chapter on interfaces in Ruby. Sandi really has a way with explaining objects; she has the knack of explaining deep principles in very easy terms; and she does it with examples. Three cheers for examples!
In this video from 2009 on SOLID Object-Oriented Design, Sandi shows how to take an everyday Ruby script and turn it into an OO thinking lesson. Please go and watch it; return when you have :-)
*
Did you notice the one wart in Sandi’s design? Yes, it’s the FtpDownloader class!! Can we get rid of the “-er” name? What would a better name be?
“Downloading” is an action. What are we doing the action to? We’re doing it to a file. So we could call the class File, but it’s not a great name. You don’t download just any file; you only download remote files. Bingo! Why don’t we call it RemoteFile? My design would have
RemoteFile.new("ftp://server/dir/file.txt").download
This gets rid of a bit of duplication in Sandi’s design. While she would have a configuration like
ftp_host: server ftp_path: /dir ftp_file: file.txt downloader_class: FtpDownloader
I would just have
file_url: ftp://server/dir/file.txt
Instead of providing multiple FtpDownloader, HttpDownloader, SftpDownloader classes I would have a single class that decides how to download based on the protocol in the url.
class RemoteFile def initialize(url) @url = URI(url) end def download self.send("#{@url.scheme}_download") end def ftp_download; ... end def sftp_download; ... end def http_download; ... end end
This makes it in my opinion more cohesive than having many single-method downloader classes. If you disagree, you could still use the trick that Sandi uses. But I would still not calling them FtpDownloader. Since they represent a protocol, I would maybe call them something like FtpProtocol. The name of a thing, not of an action.
class RemoteFile def initialize(url) @url = URI(url) end def download # if @url is "http://foo", we get HttpProtocol class_name = @url.scheme.capitalize + "Protocol" Kernel.const_get(class_name).new.get(@url) end end
If I have an FtpProtocol object, I can ask it to get or put a file at a url. It becomes a magnet for all the functionality that belongs to the Ftp protocol. This would be more cohesive that having both FtpDownloader and FtpUploader :-)
June 22nd, 2012 at 15:45
As you know Matteo, I was following Sandi’s school till recently.
Now I’m using only names without -er.
Still there is an real risk in creating God classes (Person a typical example).
The trick that works for me is to do exactly as you described here: not getting the first name but try to imagine -er classes and what they have in common.
Good post!
June 22nd, 2012 at 18:54
Great post! Thanks for linking to all those resources, I have lots of reading to do now!
I’ve been thinking about this a lot recently, after finishing the book Object Thinking. I wonder if I could get your thoughts on a few nagging issues I have though. First off, how do you square this with SRP? It looks like your RemoteFile contains the implementations for all the various different protocols. Do you think that’s even a problem?
Do you think this works in a static language too? It would be difficult to unit test this approach because you depend on the constructor of the concrete type RemoteFile.
My experience is leading me to agree with you, but it’s interesting that your advice here is basically the polar opposite of the advice given by Gary Bernhardt (in this screen cast I think: https://www.destroyallsoftware.com/screencasts/catalog/extracting-domain-objects).
Thanks!
June 23rd, 2012 at 12:32
At the base of the -er thing, which I guess has affected at least once every programmer in the world, there is a biased tendency to apply procedural design instead of OO. The latter is harder but leads to a better segregation of responsibilities. It takes more work because you have to identify and manage many more little things, but then it pays off in terms of flexibility. Besides, who loves Managers anymore? :)
June 24th, 2012 at 17:12
@Kevin: it’s true that RemoteFile will have to change whenever we need a new url scheme. Is this a problem? It depends on how often this happens. In general you can’t be closed against *every* possible change. Let me phrase this better; I don’t think it *pays* to try to be closed against any kind of change; that requires you to build lots of design that you might never need. This is why I went with the three methods ftp_download, http_download, sftp_download. If this becomes a problem, i.e., you often have to add crazy different mechanisms for downloading remote files, you would do well to move the variation out of the RemoteFile class. In my second variation I show a way to do that. So I don’t think this is a violation of SRP; I think it is a violation of OCP.
One point: RemoteFile does not really contain the implementation of the FTP protocol. It will probably delegate to library classes to do this. What my object will contain is the knowledge of how to call the library classes.
About unit testing: if I understand your objection correctly, you say that building the RemoteFile inside the PatentJob, instead of using reflection to instantiate its class from a configuration file makes it more difficult to unit test. True, but you can change Sandi’s design by passing the RemoteFile to the PatentJob in the constructor. I think this difficulty is very easy to overcome.
About Gary Bernhardt: it’s not clear to me why you say that Gary’s advice is the opposite of mine. My advice here is to find the proper names for concepts; Gary’s advice agrees with this when he creates the Catalog class.
June 25th, 2012 at 14:03
Yep, beware of -er objects!
On the RemoteFile: I would go with the protocol-based idea, so that it’s both SRP and OCP compliant.
Another great post on the topic: http://objology.blogspot.com/2011/09/one-of-best-bits-of-programming-advice.html
— Moreno