Clean code — Classes
Right now I’m reading for the second time, the great book Clean Code by Robert C. Martin aka Uncle Bob and I think it’s a good idea to write down the most important ideas that it tries to convey to us. Today, I will focus on the tenth chapter.
We like to keep our variables and utility functions private but were not fanatic about it. Sometimes we need to make a variable or utility function protected so that it can be accessed by a test.
For us, test rule. If a test in the same package needs to call a function or access a variable, we’ll make it protected or package scope. However, we'll first look for a way to maintain privacy. Loosening encapsulation is always a last resort.
Classes Should Be Small!
Smaller is the primary rule when it comes to designing classes. As with functions, our immediate question is always “How small?”
With functions, we measured size by counting physical lines. With classes, we use a different measure. We count responsibilities.
The name of a class should describe what responsibilities it fulfills. In fact, naming is probably the first way of helping determine class size. If we cannot derive a concise name for a class, then it's likely too large.
The more ambiguous the class name, the more likely it has too many responsibilities. For example, class names including weasel words like Service or Manager or Super often hint at the unfortunate aggregation of responsibilities.
We should also be able to write a brief description of the class in about 25 words, without using the words “if”, “and”, “or”, or “but”.
The Single Responsibility Principle
The SRP states that a class or module should have one and only one reason to change. This principle gives us both a definition of responsibility and guidelines for class size. Classes should have one responsibility, one reason to change (trying to identify responsibilities often helps us recognize and create better abstractions in our code).
We want our systems to be composed of many small classes, not a few large ones. Each small class encapsulates a singles responsibility, has a single reason to change, and collaborates with a few others to achieve the desired system behaviors.
Cohesion
Classes should have a small number of instance variables. Each of the methods of a class should manipulate one or more of those variables. In general, the more variables a method manipulates the more cohesive that method is to its class. A class in which each variable is used by each method is maximally cohesive.
In general, it is neither advisable nor possible to create such maximally cohesive classes, on the other hand, we would like cohesion to be high. When cohesion is high it means that the methods and variables of the class are codependent and hang together as a logical whole.
Conclusions
Needs will change, therefor code will change. We learned in OO 101 that there are concrete classes, which contain implementation details, and abstract classes, which represent concepts only.
A client class depending upon concrete details is at risk when those details change. We can introduce interfaces and abstract classes to help isolate the impact of those details.
Remember, dependencies upon concrete details create challenges for testing our system. If a system is decoupled enough to be tested in this way., it will also be more flexible and promote more reuse.