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 :-)

5 Responses to “Names objects after things, not actions!”

  1. Uberto Says:

    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!

  2. Kevin Berridge Says:

    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!

  3. Maurizio Says:

    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? :)

  4. matteo Says:

    @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.

  5. Moreno Carullo Says:

    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

Leave a Reply