5 ms·
Don't Create Objects That End with -ER
- dalke 12y agoI 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 12y agoHow about "PasswordEntries" instead?
- dalke 12y agoThat'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 12y agoThe 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 12y ago
- dragonwriter 12y ago> [...] 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 12y agoI 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.