Pages

7.29.2013

Notes on... Clean Code by Robert C. Martin

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