More on this book
Community
Kindle Notes & Highlights
Unit tests should be written just before the production code that makes them pass.
With functions we measured size by counting physical lines. With classes we use a different measure. We count responsibilities.1
The name of a class should describe what responsibilities it fulfills.
The more ambiguous the class name, the more likely it has too many 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 (SRP)2 states that a class or module should have one, and only one, reason to change.
Trying to identify responsibilities (reasons to change) often helps us recognize and create better abstractions in our code.
However, a system with many small classes has no more moving parts than a system with a few large classes.
We want our systems to be composed of many small classes, not a few large ones.
Classes should have a small number of instance variables.
A class in which each variable is used by each method is maximally cohesive.
When cohesion is high, it means that the methods and variables of the class are co-dependent and hang together as a logical whole.
You should try to separate the variables and methods into two or more classes such that the new classes are more cohesive.
If there are a few functions that want to share certain variables, doesn’t that make them a class in their own right? Of course it does. When classes lose cohesion, split them!
So breaking a large function into many smaller functions often gives us the opportunity to split several smaller classes out as well.
Every change subjects us to the risk that the remainder of the system no longer works as intended.
Private method behavior that applies only to a small subset of a class can be a useful heuristic for spotting potential areas for improvement.
In an ideal system, we incorporate new features by extending the system, not by making modifications to existing code.
Inversion of Control moves secondary responsibilities from an object to other objects that are dedicated to the purpose, thereby supporting the Single Responsibility Principle.
A premature decision is a decision made with suboptimal knowledge.
Writing tests leads to better designs.
Understanding how to achieve reuse in the small is essential to achieving reuse in the large.
It’s easy to write code that we understand, because at the time we write it we’re deep in an understanding of the problem we’re trying to solve.
A good way to avoid shared data is to avoid sharing the data in the first place.
Avoid using more than one method on a shared object.
The synchronized keyword introduces a lock.
All sections of code guarded by the same lock are guaranteed to have only one thread executing through them at any given time.
A critical section is any section of code that must be protected from simultaneous use for the program to be correct.
Keep your synchronized sections as small as possible.
Testing does not guarantee correctness.
Treat spurious failures as candidate threading issues.
Bugs in threaded code might exhibit their symptoms once in a thousand, or a million, executions.
To encourage task swapping, run with more threads than processors or cores.
Multithreaded code behaves differently in different environments.
The reason that threading bugs can be infrequent, sporadic, and hard to repeat, is that only a very few pathways out of the many thousands of possible pathways through a vulnerable section actually fail.
Learn your library and know the fundamental algorithms.
To write clean code, you must first write dirty code and then clean it.
One of the best ways to ruin a program is to make massive changes to its structure in the name of improvement.
What I am about to do is nothing more and nothing less than a professional review. It is something that we should all be comfortable doing. And it is something we should welcome when it is done for us.
It’s generally a bad idea for base classes to know about their derivatives.
Eliminating final flies in the face of some conventional wisdom.
Comments should be reserved for technical notes about the code and design.
A comment is redundant if it describes something that adequately describes itself.
Comments should say things that the code cannot say for itself.
Commented-out code is an abomination.
No argument is best, followed by one, two, and three.
Output arguments are counterintuitive.
If your function must change the state of something, have it change the state of the object it is called on.
Boolean arguments loudly declare that the function does more than one thing.
There is no replacement for due diligence.