Table of Contents
- Getting Clean via Emergent Design
- Runs All Tests
- Refactoring
- No Duplication
- Expressive
- Minimal Classes and Methods
Getting Clean via Emergent Design
- 4 Rules to help us apply the Single Responsibility Principle and the Dependency Inversion Principle to our code to make good designs, in order of importance:
- Runs all the tests.
- Contains no duplication.
- Expresses the intent of the programmer.
- Minimises the number of classes and methods.
Runs All Tests
- A system must be able to be verified that it works as intended.
- This is achieved through tests, they should act like a spec of how the system should work.
- This in turn verifies whether or not the system does in fact work.
- A system that can't be verified should not be deployed.
- Making a system testable automatically encourages you to make classes and functions smaller to fit the single responsibility principle.
- Tight coupling makes it difficult to write tests, hence the more tests we write, the more we will end up using the dependency inversion principle to avoid this coupling. (through the use of interfaces, dependency injection and abstraction.)
- Ultimately writing tests leads to better designs.
Refactoring
- For every few lines of code we add, we pause and reflect on these changes. With our test running to ensure we are not breaking code.
- This allows us to continue to build and refactor without fear of breaking what is currently working.
- During refactoring we can increase cohesion, decrease coupling, separate concerns, modularize systems concerns, shrink our functions and classes, choose better names and so on.
- It is within this step of refactoring code where we apply the last 3 rules of emergent design (eliminate duplication, ensure expressiveness, and minimise the number of classes and methods).
No Duplication
-
Represents additional work, additional risk and additional unnecessary complexity.
-
Lines of code that look exactly like are duplication.
-
Lines of code that are similar can often be massaged to look even more a like and will be found to in fact be duplication.
-
An example of potential duplication can be seen below, you can see that the 2 functions will be similar in implementation.
int size() {} boolean isEmpty() {} // Improved without duplication int size() {} boolean isEmpty() { return 0 == size(); }
-
Understanding how to achieve reuse in the small is essential to achieving reuse in the large.
Expressive
- The majority of the cost of a software project is in long-term maintenance.
- We can reduce this cost by taking the extra time to make sure that code is clean after we have got it working.
- We can express ourselves by:
- Choosing good names: we want to be able to hear a class or function name and not be surprised when we discover its responsibilities.
- Keeping functions and classes small: this makes them easier to write, name and understand.
- Using standard nomenclature.
- Well-written unit tests.
- Most importantly, the biggest way to be expressive is to genuinely try to be expressive.
Minimal Classes and Methods
- Although we want our classes and functions small, we don't want them excessively small.
- This is achieved by adding the rule of not having too many functions and classes.
- If we abide by this general rule, it will avoid us having many tiny classes and functions all over the place.
- We want to avoid pointless dogmatism.