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.
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.
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.
All objects are born equal. Our designs 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
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.
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.
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.
Rule 13: There are no setXXX or getXXX methods.
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.
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 are always present in software development.
There’s the rubber duck debugging technique. and this is the duck typing technique
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 name objects according to that responsibility.
Rule 16: Use names after observing behavior.
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.
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.
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
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.
Their 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.
Being 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.
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.