Clean Code by Robert C. Martin
I recommend buying and reading this book more than once. This is a summary of what is included:
[ PART I ]
Chapter 1: Authors
- I am an author, writing for readers who will judge your effort.
- How to clean up the code? Leave the campground cleaner than you found it.!
Chapter 2: Meaningful Names
- Names should reveal intent - take care when choosing your names and change them when you find better ones. Can’t do that with a kid.
- Magic Numbers - Unique values with unexplained meaning or multiple occurrences which could (preferably) be replaced with named constants.
- Avoid Disinformation - keep in mind that certain names may cause confusion or have a different meaning for different people.
- Make Meaningful distinctions - different variable or methods names with a clear difference of what they are or do.
- User pronounceable Names - just because.
- Use searchable names - length of a name should correspond to the size of its scope.
- Avoid Encodings -
- Hungarian Notation
- Member Prefixes
- Interfaces and Implementations prefix
- Avoid mental mapping
- Class Names - noun or noun phrase names
- Method Names - verb or verb phrase names
- Overloaded constructors
- Pick one word per concept: get, fetch, retrieve
- Don’t pun: avoid using one term for different purposes.
- Use solution domain names
- Add meaningful context
- Shorter names are “generally” better than longer ones
Chapter 3: Methods
- Small - less than 20 lines long. :)
- Blocks and Indenting - (if, else, while) should be one line long and that line be a method call.
- Do 1 thing - methods should do one thing and do it well. Can you extract another method from it with a different name? Methods that do 1 thing cannot be divided into sections. Avoid coupling.
- One level of abstraction per method
- Step down Rule - code should read like a top down narrative, descending levels of abstraction.
- Switch Statements - avoid them and if can’t, use in an abstract factory (appear once, are used to create polymorphic objects, and hide behind an inheritance).
- Use Descriptive Names - what is the 1 thing the method is doing? Name it properly.
- Method Arguments - Ideal number of arguments is 0 and 3 or more should be avoided.
- Monadic Methods: (1 parameter) verifies something or transforms something and if so, returns a value.
- Avoid flag arguments: (passing a boolean as parameter)
- Dyadic Methods: (2 parameters) avoid when possible, use 2 parameters that make sense together.
- Triad Methods: avoid.
When a function seems to need more than 2 or 3 arguments, some of those arguments ought to be wrapped in a class of their own.
- Argument Objects: pass in an object as a parameter instead of many fields.
- Choose smart verb noun pairs: ie. Write(name).
- Avoid Output Arguments - If your function must change the state of something, have it change the state of its owning object.
- Command Query Separation - a method doing something shouldn’t return a boolean. It causes confusion of whether it performs a query or a command.
- Exceptions instead of returning error code
- Handle exceptions
- Extract body of try catch into a method to avoid confusion.
- Error handling is 1 thing. so start method with try and end with catch or finally.
- Don’t repeat yourself
- Structured Programming - this applies for big methods
- One return statement in a function
- no break or continue in a loop
- and never a goto statement
Chapter 4 : Comments
In summary, a comment is good if it describes a class or explains unique functionality of the class.
- Compensate for our failure to express ourselves in code.
- explain yourself in code
- clean the mess instead of commenting it
- Good Comments
- Legal comments
- Informative comments (?)
- Explanation of Intent
- Clarification (not your code, but part of a lib and you can’t modify)
- Warnings of consequence
- TODO comments
- Amplifications
- Javadocs in public APIs
- Bad Comments
- Mumbling
- Redundant comment
- Misleading comments
- Mandate comments - force to add comments when not needed
- Journal Comments - log with changes
- Noisy comments
- Don’t use comment when you can use a method or field
- Position markers
- Closing brace comments
- Attributions and Bylines
- Commented-out code
- too much info, unlocal comments
- function headers - choose a good function name
- Javadoc in nonpublic code
Chapter 5 : Formatting
Formatting for our codes to be clean and easy to communicate. Individuals/Teams should set a list of rules to follow through code.
- Vertical formatting - small files are easier to understand. 200 lines per file or 500 as max.
- Newspaper metaphor. More details as your read on.
- Vertical Openness - Separate thoughts with blank lines.
- Vertical Density - unite blocks
- Vertical Distance - related concepts should be vertically close to each other.
- Variable declarations - declare variables as close to their usage as possible.
- Instance variables - declared on the top of the class.
- Dependent methods - placed close and caller above callee.
- Conceptual affinity - the stronger the affinity the closer they should be.
- Horizontal Formatting - between 80 and 200 characters per line.
- Keep it neat
- No alignment
- Use spaces and indentation where necessary
Chapter 6 : Objects and Data Structures
- Data Abstraction - Hiding implementation is not just a matter of putting a layer of functions between the variables but about abstractions.
- Data/Object Anti-Symmetry - Objects hide their data behind abstractions and expose functions that operate on that data (getters and setters simply get the data).
- Law of Demeter - a method m of class C should only call the methods of:
- C
- An object created by f or passed as argument to f
- An object held in an instance variable of C
- A method should not invoke methods on objects returned by the allowed function.
- Train wreck code - avoid coupled code
final String outputDir = ctxt.getOptions().getScratchDir().getAbsolutePath();
- Avoid Hybrids - half object and half data structure
- Data Transfer Object -
- DTOs
- Active Records
Treat DTOs as data structures and use other objects with the business rules.
Chapter 7 : Error Handling
- Use exceptions rather than return codes
- Write try-catch statement first - build the transaction scope of the try first and maintain the transaction nature of the scope.
- Use unchecked exceptions - checked exceptions thrown at the lowest level result in a cascade of changes breaking encapsulation.
- Oracle - If a client can reasonably be expected to recover from an exception, make it a checked exception. If a client cannot do anything to recover from the exception, make it an unchecked exception.
- Provide context with the exception - message should contain the operation that failed and the type of failure.
- Define exception classes in terms of the caller’s needs - if multiple exceptions are thrown and the catch is the same for all:
- Wrap third-party APIs (pg 140) or
- catch Exception and have one catch for all exceptions.
- Define the normal flow -
- Special case pattern (Fowler) - create a class to handle the special exceptions for you.
- Don’t return null and don’t pass null
- Use special case objects, i.e. Collections.emptyList()
Chapter 8 : Boundaries
Integrating foreign code with ours. Practices and techniques to keep the boundaries of our software clean.
- Third party code - when using boundary interfaces, keep it inside a class or close family of classes.
Map ie.:
Sensor s = (Sensor)sensors.get(sensorId ); //Casting and not as readable
...
Map<Sensor> sensors = new HashMap<Sensor>();
Sensor s = sensors.get(sensorId ); //Readability improved, yet too flexible
…
public class Sensors {
private Map sensors = new HashMap();
public Sensor getById(String id) {
return (Sensor) sensors.get(id);
}
} // The class manages the casting and type of the Sensors object and the use of the Map class is restricted by the class.
- Write “learning tests” for third party code - makes the learning and coding a bit easier.
- learn the API
- test for new versions of the API
- test the interface in the same way your code will use it
- Using code that doesn’t exist (unknown)
Chapter 9 : Unit Tests
Test code is as important as production code. It keeps our code flexible and allows us to make changes without worries.
- Clean Tests - are readable
- Build-Operate-Check pattern
- Domain specific test language - Build your own testing API
- One Assert per Test (easier to understand - but not the best option always)
- Single Concept per Test
- FIRST - Fast, Independent, Repeatable, Self-validating, Timely
Chapter 10 : Classes
- Class Organization - Variables first - public static constants, private static variables, private instance variables, public functions, private utilities under function used in.
- Encapsulation - variables and utility functions private, unless needed by a test.
- Classes should be small - count responsibilities.
- The name should reflect the responsibility it fulfills.
- Single Responsibility Principle (SRP) - a class should have one reason to change.
- Cohesion - methods in a class should manipulate 1 or 2 class fields.
- When classes lose cohesion, split them.
- Open-Closed Principle (OCP) - classes should be open for extension but closed for modification (organize our classes in a way that change doesn’t affect them).
- Dependency Inversion Principle (DIP) - our classes should depend on abstraction, not concrete details (Isolating from change).
Chapter 11 : Systems
Chapter 12 : Emergence
- Four Rules of Simple Design: A design is “simple” if
- Runs all tests
- Contains no duplication
- Expresses the intent of the programmer
- Minimizes the number of classes and method
- Rule 1: Runs all tests - tests are a way to verify that a system runs at intended.
- A testable system is a reliable one.
- Writing tests leads to better design.
- Simple Design Rules 2-4: Refactoring
- Incrementally refactor the code
- Rule 2: No Duplication -
- similar lines of code
- methods that have similar implementations
- Rule 3: Expressive -
- Choose good names (name show class responsibility)
- Keep functions and classes small
- Use standard nomenclature (ie. Design Patterns)
- Well written tests
- CARE and TRY
- Minimal classes and methods -
- Small classes and methods but not too many classes and methods.
Chapter 13 : Concurrency
Concurrency defence Principles:
- Single Responsibility Principle - single reason to change. Keep concurrency code separate from other code.
- Limit the scope of data - encapsulation! limit access of data that might be shared.
- Use copies of data - to avoid sharing.
- Threads should be as independent as possible - each thread should be independent and not share data with other threads.
- Know the thread Library/API you’re using - For java review java.util.concurrent, java.util.concurrent.atomic, java.util.concurrent.locks.
- Know your execution model -
- Bound Resources - Resources of a fixed size or number used in a concurrent environment.
- Mutual Exclusion - Only one thread can access shared data or a shared resource at a time.
- Starvation - One thread or a group of threads is prohibited from proceeding
- for an excessively long time or forever.
- Deadlock - Two or more threads waiting for each other to finish.
- Livelock Threads - in lockstep, each trying to do work but finding another
- “in the way.”
- Dependencies between synchronized methods - avoid using more than one synchronized method in a class.
- Keep your synchronized sections as small as possible.
- Shut-down code - Think about shut-down early and get it working early. It’s going to take longer than you expect. Review existing algorithms because this is probably harder than you think.
[ PART II ]
Stay tuned for Part II.
Chapter 14 : Successive Refinement
Chapter 15 : JUnit Internals
Chapter 16 : Refactoring SerialDate
Chapter 17 : Smells and Heuristics
No comments:
Post a Comment