
Don't Create Objects That End with -ER - yegor256a
http://www.yegor256.com/2015/03/09/objects-end-with-er.html?2015-10
======
dragonwriter
> [...] Validator. [...] What do they have in common? They all end in "-er".

Nope, try again.

> They are not classes, and the objects they instantiate are not objects.

They are classes, and the objects that are instances of them are objects. In
fact, they are often the _best_ objects -- by SOLID standards -- as their
names tend to hint: their names reflect their role as agents directed at a
single purpose -- that is, that they adhere to the Single Responsibility
Principle.

> The main argument against this "-er" suffix is that "when you need a
> manager, it's often a sign that the managed are just plain old data
> structures and that the manager is the smart procedure doing the real work".

"Manager" in particular is a name that signals trouble, because "manage" is a
vague term under which multiple different, poorly connected responsibilities
are often lumped.

> When you add the "-er" suffix to your class name, you're immediately turning
> it into a dumb imperative executor of your will. You do not allow it to
> think and improvise. You expect it to do exactly what you want — sort,
> manage, control, print, write, combine, concatenate, etc.

You may have a coherent point here, but its hidden under some really bad
anthropomorphizing metaphor -- obviously, unless I'm designing fancy-pants AI
code, I don't what my code -- classes or otherwise -- to think, improvise, or
in any other way do anything other than do exactly what I want. So portraying
it not doing any of those unwanted things as a reason _not_ to engage in a
practice is, well, not convincing.

This entire article suffers from:

1) A lack of concrete definition of the problem is,

2) A lack of concrete examples of the problem that demonstrate that, indeed,
it is a problem, and

3) A recommendation (with examples) of what would be good to do instead of the
problem.

~~~
dalke
I think the submitter, which appears to be the author of the essay given the
account and host names, doesn't care about promoting a discussion.

The account has posted self-hosted essays about every two days for almost a
year. They garner almost no comments, and no followup from the submitter.
Instead, the account gets downvotes, and has an average karma of .81.

This suggests someone using HN mostly for traffic promotion, and is unlikely
to respond to either of our comments.

I agree that your points and conclusions are good ones.

The "-or" vs. "-er" observation is weak; it's a historical relic of how we
treat Latin vs. Germanic words. Had the author been more well informed on
grammar, the essay would have refered to agent nouns, and included "beggar"
and "liar" as two of the rare non-er/non-or cases.

~~~
yegor256a
I do read your responses :) But most of them (like the one before) doesn't
expect my answers. It's just an opinion that contradicts with my article. I
read it and I understood it. There are no questions asked, so no answer given.

------
dalke
I don't understand why "reader" is a bad name. One of the links says:

> Reader/Writer – The main problem I have with these types of objects is that
> it only describes a small portion of the interaction that an object can
> have. A PhotoWriter is not an object, rather a Photo as an object has
> functionality binded to it so that it can be read or written.

I don't get that though.

Here's an example reader for the /etc/passwd file on my system. The reader
iterates over PasswordEntry records, and the reader exposes a "Location"
interface to get access to reader state, like the line number, which isn't
logically part of the PasswordEntry. The reader also implements the context-
manager and iterator APIs:

    
    
        from collections import namedtuple
    
        PasswordEntry = namedtuple("PasswordEntry",
                                   "name password uid gid gecos home_dir shell")
        class Location(object):
            def __init__(self, filename, lineno=0, recno=0):
                self.filename, self.lineno, self.recno = filename, lineno, recno
    
        class PasswordReader(object):
            def __init__(self, infile, reader_iter, location):
                self._infile = infile
                self._reader_iter = reader_iter
                self.location = location
            def __iter__(self):
                return self._reader_iter
            def __next__(self): # Python 3
                return next(self._reader_iter)
            def next(self): # Python 2
                return next(self._reader_iter)
            def close(self):
                self._infile.close()
            def __enter__(self):  
                return self
            def __exit__(self, *args):
                self.close()
    
        def _read_password_entries(infile, location):
            recno = 0
            for lineno, line in enumerate(infile, 1):
                location.lineno = lineno
                if line.startswith("#"):
                    continue
                fields = line.rstrip().split(":")
                location.recno = recno = recno + 1
                yield PasswordEntry(*fields)
    
        def read_password_entries(filename):
            infile = open(filename)
            location = Location(filename)
            reader_iter = _read_password_entries(infile, location)
            return PasswordReader(infile, _read_password_entries(infile, location), location)
    
        # Example code using the API
        with read_password_entries("/etc/passwd") as reader:
            for entry in reader:
                if entry.shell != "/usr/bin/false":
                    print("Entry %r (record #%d, line %d) uses shell %r" % (
                        entry.name, reader.location.recno, reader.location.lineno, entry.shell))
    
        ## output for my system
        # Entry 'root' (record #2, line 12) uses shell '/bin/sh'
        # Entry '_uucp' (record #4, line 14) uses shell '/usr/sbin/uucico'
    

What's a better name than "PasswordReader"? (Other than PasswordEntryReader; I
mean something which doesn't end in "-er".)

I don't understand the objection "it only describes a small portion of the
interaction that an object can have". This is a reader, no? It reads entries,
and that's pretty much all the interaction it has.

> They are not classes, and the objects they instantiate are not objects.
> Instead, they are collections of procedures pretending to be classes.

I also didn't understand this. How is the PasswordReader "pretending to be a
class". It is a class, and methods like the context manager methods have to be
implemented through a class, and not a "collection of procedures."

(Of course, C++ as "C with classes" is an example of OO programming as data
structures plus a collection of procedures, where the language makes it easier
to tie the two together. But that can't be the objection as then all C++ OO
code is "pretending to be a class", making this an argument from extreme
purism rather than one with more practical implications.)

~~~
yegor256a
How about "PasswordEntries" instead?

~~~
dalke
That's more often used for a collection with random-access lookup, like a list
of PasswordEntry instances. A specialized class might be indexed for lookups,
as in:

    
    
        class PasswordEntries(object):
            def __init__(self, entries):
                self.entries = entries
                self._by_name = {entry.name: entry for entry in entries}
                # Also add lookup by uid, gid, etc.
            def __len__(self): return len(self.entries)
            def __getitem__(self, i): return self.entries[i]
            def get_name(self, name): return self._by_name.get(name)
            #  other public lookup methods here
    
        entries = PasswordEntries(read_password_entries("/etc/passwd"))
        print "root uses", entries.get_name("root").shell
        print "_lp uses", entries.get_name("_lp").shell
    
    

It's a bad name for a reader, which is more closely tied with I/O. The suffix
"-reader" suggests support for file operations (like 'close()'), use as a
context manager, and awareness that the values will only be available once.

The suffix "-s" does not. At the very least it suggests that it's possible to
start a new iteration over the complete container of entries.

While you work to come up with a better name than "PasswdReader", what is your
replacement name for "generator", as in:

    
    
        >>> def f():
        ...   yield 1
        ... 
        >>> x = f()
        >>> x
        <generator object f at 0x103ac3f50>
        >>> x.__class__
        <type 'generator'>
    

Your essay places the -or names into the -er category (they are, after all,
indicators of action nouns), so why is "generator" a bad name?

~~~
yegor256a
The idea of object-oriented programming is that you, a client of that reader,
should NOT know that it reads from I/O. You should not rely on this
information. You get a list of entries. Where they are coming from, either
I/O, memory or a user console, is none of your business. It's an internal
information of the object that gives them to you.

~~~
dalke
Your view is completely unsupported by the evidence. At some point you have to
work with the filesystem, or the network, or other I/O systems. These have OO
interfaces. According to you, these don't exist, or are somehow not OO. It's a
purist attitude that does not fit reality.

You must know the performance characteristics of your API. OO says nothing
about performance. Sure, it's possible to have a random-access/list-like
interface to a set of records ... on a file system, and assuming you're
willing to take the initial hit for indexing should you want to seek to the
end. But this assumes you are working with a file, and not a tape drive,
socket, pipe, keyboard or other interface.

It also assume that memory is infinite. You do not get a "list of entries",
you get an iterator (notice the "-or"!) of records. A reader doesn't know if
you have enough memory to provide a list. A list does, or it had to provide a
lot of work to fake it.

If you don't care about performance, memory constraints, latency, and other
real-world factors then sure, your idealized world could do what you want. I
don't live in that world, nor do you, nor does it seem that we'll get there in
our lifetimes, nor does it seem reasonable to get rid of readers for the
contexts I've mentioned, nor does it seem that people are confused by the
"reader" suffix.

Again, I ask for your names that distinguish between something which reads
from a streaming interface (what I call a reader) and something which supports
random access lookup (what I call a list). I gave code; they do different
things. You suggsted "list" for the first one; what do you call them?

I also asked you for what you think is the right name for a "generator".

~~~
yegor256a
Let's discuss one situation at a time. Which one bothers you most? The reader,
the iterator, the generator or the list of entries? Give me one practical
example of that -ER object and I'll show you how it can be refactored to get
rid of this suffix and become more object oriented.

~~~
dalke
Is this a delaying tactic? I started with the reader, I gave working code
named PasswdReader. You suggested the alternate name PasswdList so I replied
with something that was a better fit to then name PasswdList, in large part
because it had list-like performance characteristics.

I think it's safe to assume that "reader" is the one that 'bothers me the
most'.

As for "practical", it's a simplification of code I use for parsing a chemical
structure file into a sequence of records/molecule objects. I simplified it so
it would fit in an HN comment and not require specialized domain knowledge. It
does reflect the true intent of practical code.

I'll be surprised if you can come up with a better name for "-Reader". Once
that's done, I would like to read about your alternative name for "generator".

But I can point to read-world OO interfaces like ANTLR at
[https://theantlrguy.atlassian.net/wiki/display/ANTLR4/Parse+...](https://theantlrguy.atlassian.net/wiki/display/ANTLR4/Parse+Tree+Listeners)
which uses "ParseTreeListener", "JavaLexer", "JavaParser", and
"ParseTreeWalker". It's much easier for me to believe you don't know what you
are talking about than to believe that all of these "-er" classes reflect bad
OO design.

