Clean Code
Clean Code: Reading Notes
This is my document to write down things I feel like are important from the book Clean Code
Useful patterns
Template Pattern
The template pattern is useful for situations where you have many similarities between entities, with small differences. These can easily be separated by creating an abstract class with all commonalities combined. Any differences are represented by abstract methods, which are then implemented by subclasses. For example:
public abstract class Employee {
protected float dayStarted;
protected float timeWorked;
void startDay() {
if (dayStarted != 0f) {
throw new IllegalStateException();
}
dayStarted = timeNow();
}
void endDay() {
timeWorked += timeNow() - dayStarted;
dayStarted = 0f;
}
abstract void pay();
}
// paid a fixed amount
// time worked is used to keep track of overtime, etc.
public class NineToFiveEmployee {
private Money monthlyPay;
void pay() {
giveMoney(monthlyPay);
}
}
// paid by time, so time worked is used for pay
public class Contractor {
private Money hourlyPay;
private float timeWorked;
void pay() {
giveMoney(hourlyPay * timeWorked);
}
}
Conceptual Notes
A Big Reason Why Code Is Unclean, Is That No Effort Was Made To Clean It
The first step in writing code, is to understand the problem. This includes the requirements, the environment, and the code.
The second step in writing code is figuring out a solution, and putting it to paper. This means writing tests and writing production code.
However, there is also a third step, which is often skipped, cleaning up the code.
The first solution is not usually the best solution. This isn't a problem, nor does it only apply novice programmers, it is a general tendency. While programmers work, they try things out, until it works. This may leave unused methods, unnecessary fields, etc. These are often removed, but there is usually higher level duplication and poor design which has accumulated during development. The third step in writing code, then, is to go back and make your code clean.
OO And Functional/Procedural Programming:
I think the book has a fantastic explanation of the difference between OO and procedural programming. The example is of different shapes and calculating the area.
In procedural programming, there are three objects, square, rectangle and circle, and one function: calculate area. Depending on which object is passed, the values from the objects are taken in order to calculate the area.
In the OO version, there are three objects, each of which implements an interface shape, with a function for calculating their own area.
This highlights the fascinating difference between them: In OO, it is easy to add new objects without changing procedures, but in Procedural Programming, it is easy to add new functions without changing any objects.
The inverse is also true, in OO it is hard to add new procedures (to the interface), and in PP, it is hard to add new objects without changing procedures.
This can act as a guide as to which style to use for your problem.
To quote: "Objects expose behavior and hide data. This makes it easy to add new kinds of objects without changing existing behaviors. It also makes it hard to add new behaviors to existing objects. Data structures expose data and have no significant behavior. This makes it easy to add new behaviors to existing data structures but makes it hard to add new data structures to existing functions."
Classes
Every class should have one responsibility (Single Responsibility Principle). In other words, there should only be one reason that a class changes.
For example, imagine a class that prints the first x primes. This could all be done in one class, but that would violate the SRP. In order to do it well, it could be split up into three classes: A managing class (ie PrimePrinter) - changes if the environment changes. A printing class (ie RowPrinter) - changes if the formatting should change. A prime generating class (ie PrimeGenerator) - changes if the prime generation algorithm changes. Each of these classes has one responsibility, and one reason to change.
Unit Tests
Unit tests are the thing that keeps your code flexible, maintainable, and reusable.
The reason for this is that without unit tests, any change may break some functionality. This method has an unexpected side effect? Maybe some caller expects it - real example: save Action also sends push This class has an unused field? Maybe it's used while serializing HTTP responses - real example: w/o IntelliJ ultimate, Spring features are not highlighted as being used
Unit tests allow us to make changes without being afraid of breaking something in the process. This means that bad tests impede progress.
Tests should be: F ast (or you won't run them often) I ndependent (from other tests) R epeatable (any environment) S elf-validating (shouldn't need to read logs to see if tests passed) T imely (written before the production code)
Use Unchecked Exceptions
Checked exceptions break encapsulation. Only use them in critical modules (eg security).
No comments to display
No comments to display