We all agree: a good name is always the most important thing. Let's find them.
We all use names for programming, it doesn't matter if the language is high or low level, whether it is Imperative, Functional or Object-Oriented. Names are everywhere. But we continue to misuse them. In this second part we will see how to change some habits.
The search is ongoing
In a previous article, we introduced various definitions and techniques for looking for good names. In this note we will try to show some present problems on nomenclature in order to improve our practices.
What exactly is a name? — Part I: The Quest
Maxi Contieri ・ Feb 9 '21
We do not need help
All objects help, There are no non-supportive objects.
In the real world there are no helpers.
We have a single design rule. If a concept does not exist in the real world, and we cannot explain it to a domain expert, that object must not exist.
Photo by Big Dodzy on Unsplash
Rule 9: Helpers don't exist
We don't need bosses
All objects are born equal. Our designed objects will have social equality. There are no managers. There are objects with different responsibilities.
In the real world there are no managers (unless we are modeling job roles).
Rule 10: Managers do not exist.
Photo by Amy Hirschi on Unsplash
We don't need to say "objects."
Objects are omnipresent. Naming a class by saying Object is a code smell like the ones mentioned above.
Rule 11: Objects don't exist. They all are.
All your Base are belong to us
Unless we are modeling a space or military system we should not name our classes with the name Base.
Base stands for the absence of a real world abstract concept and the chances we are reusing code through inheritance. Yet another code smell.
This violates the design principle that suggests we favor the (dynamic) composition of objects over the (static) inheritance of classes.
Rule 12: Base objects do not exist.
We don't need accessors
As we saw in previous articles, having setters and getters leads to encapsulation violation and misassignments of responsibilities. We should be suspicious of all getXXX() or setXXX() functions.
We do not usually find these responsibilities in domain entities in the bijection to the real world. There are no real set() and get() responsibilities in business models.
In the accidental case of matching a responsibility with an attribute we will call the function in the same way without the get() prefix.
<?
class Polygon {
function getVertices(): Collection { //Wrong
//...
}
function vertices(): Collection { //Right
//...
}
}
Rule 13: There are no setXXX or getXXX methods.
Don't ask who I am
Functions that start with isXXXX() are usually implementative.
They ask for a type (avoiding the double dispatch pattern), generate coupling and are always followed by an if.
As a general rule, we should restrict the use of Booleans to situations where such Booleans exist in the real world.
As a corollary, thinking on the MAPPER, distrust the isXXX() methods.
Rule 14: There are no isXXX () methods.
If it smells like interface it is Interface
Names like iterable, serializable, etc. preach about object's responsibilities. They will be excellent interface names and therefore we should not use them to name classes.
Rule 15: The names able remain for the interfaces (therefore they cannot be instantiated)
Ducks on a pond
Ducks are always present in software development.
There's the rubber duck debugging technique. And this is the duck typing technique
Photo by Jordan M. Lomibao on Unsplash
When I see a bird that walks like a duck, it swims like a duck and sounds like a duck, I call that bird a duck.
James Whitcomb Riley
This technique suggests that we know objects by their responsibilities and their context role. A corollary states we should to name objects according to that responsibility.
Rule 16: Use names after observing behavior.
Naming patterns
The greatest benefit from known design patterns is the unification of a common language.
We all know what a decorator, a strategy, an adapter or a facade is. And we know that you should never use Singletons:
Rule 17: Use pattern names for implementing concepts.
If it is abstract, it has an abstract name.
Our language is very rich and has many words that model concepts. If we want to group concepts using the Aristotelian classification technique, we will name the classes with those names.
This is the classic example to show inheritance:
The common superclass of {Car, Boat, Airplane} Should not be AbstractCar or BaseCar, neither BaseBoat nor Movable.
It should be: Vehicle.
Rule 18: Abstract names must be discovered. Not invented.
Corollary 18: Do not use the word abstract as part of a name.
Responsibility is the best name
The best rule for naming a class is finding its counterpart in the bijection. Should this be hard, we should understand the concept based on their responsibilities.
Let's see an example:
If it can be traversed, it can be added and it can be removed, it is not an ArrayHelper, not an ArrayManager, not a BaseArray, much less an ArrayObject.
For example when trying to group: Array, Set, LinkedList, Multiset, Stack, Queue etc. we can derive the word that best describes them all.
Its main responsibility is to collect items, therefore we are in the presence of a Collection.
We will learn this only after knowing many of its subclasses using the Liskov Substitution Principle (the L for Solid).
Rule 19: In order to name concepts we must know their protocol.
We speak the same language
As myself a native Spanish speaker teaching at a University, students often ask us if they should assign names in Spanish (the business language) or in English (the base language of programming languages).
If we are to be consistent with the polymorphism of the functions, we must always use the same language.
For the foreach() iterator to be polymorphic with all iterable objects it must have its name in English.
If we create an object with a recorrer() function (traverse in spanish), we will lose the polymorphism, and we will have to couple with an if rule.
20: The code must be written in English.
Rules Summary
- Names must be declarative and not implementing.
- Names must be contextual.
- Do not mix type with role.
- Do not use setters or getters.
- Do not use non-existent terms such as Manager, Helper, Base.
- Do not use too generic terms, such as Object.
- Assign responsibilities before assigning names.
- When in doubt, put bad names.
- Avoid comments.
Part of the objective of this series of articles is to generate spaces for debate and discussion on software design.
We look forward to comments and suggestions on this article.
You can hit me up on twitter.