Upgrade to Pro — share decks privately, control downloads, hide ads and more …

2014 Graduate Bootcamp: Clean Code

2014 Graduate Bootcamp: Clean Code

Nicer to work with.
Less likely to have bugs.
Most importantly: easy to change.

Don’t add something because it might come in handy!
Outside-in TDD helps.

Regexps are the same.
What if you find a bug? What if they’re miles apart? Can you guarantee you’ll change both?

Extract duplicate code so each piece of knowledge is represented in one place.
Give it a name that makes it clear what it does.
You could also argue that there’s still duplication: only the field changes.

Is this the same? No!
Different concepts – incidental duplication.
If you extracted the regexp and order ID format changed, you’d break EIN validation.

Three responsibilities.
If one changes, need to modify the class. Potential to break stuff.

Employee class is now only responsible for persistence.

Introduce separate classes for other responsibilities.

Sounds crazy, right?

Currently we want to log an event whenever an employee is added.
What if we also wanted to send an email to the payroll team, or notify some other system? We’s need to modify this class.
Same issue as before – changing a class risks introducing bugs.

Allow registration of any number of listeners…

…then notify them all.
No need to change the class.

Square is a subclass of Rectangle.
Seems legit – a square is a rectangle, after all.

No complaints from the compiler, because Square is a valid Rectangle.

Hmm.

Turns out in this case it’s not valid for Square to be a subclass of Rectangle.
In this specific case, if the classes were immutable there wouldn’t be a problem.

Do we really want to risk code related to sending newsletters accidentally giving someone a payrise?
A bit contrived, but if we expose all an object’s methods everywhere, it’s easy for functionality to creep into the wrong places.

Narrow interfaces.

Employee now implements both.

Methods can be restricted to just the relevant interface.

Not going to go into detail about exactly what this means, but just a simple example.

EmployeeRegistry (high level) depends on Database (low level and specific).
What if we want to change database, or use a fake for testing?

DbConnection is an abstraction.
We now inject the connection into the constructor, so it can be replaced with a different implementation without changing the registry class.

Intent: each piece of the system should do one thing only, and make it clear what that is.
Duplication: concepts as well as actual chunks of code.
Ordered, highest priority first.
Good names better than comments.

Originally for Ruby.
Maybe add a few lines for Java because it’s more verbose.

Kerry Buckley

October 27, 2014
Tweet

More Decks by Kerry Buckley

Other Decks in Programming

Transcript

  1. public boolean hasValidSourceIp() { return sourceIp.matches( "\\A(?:\\d{1,3}\\.){3}\\d{1,3}\\Z" ); } public

    boolean hasValidDestinationIp() { return destinationIp.matches( "\\A(?:\\d{1,3}\\.){3}\\d{1,3}\\Z" ); }
  2. public boolean hasValidSourceIp() { return sourceIp.matches( "\\A(?:\\d{1,3}\\.){3}\\d{1,3}\\Z" ); } public

    boolean hasValidDestinationIp() { return destinationIp.matches( "\\A(?:\\d{1,3}\\.){3}\\d{1,3}\\Z" ); }
  3. private String IP_ADDR_PATTERN = "\\A(?:\\d{1,3}\\.){3}\\d{1,3}\\Z" public boolean hasValidSourceIp() { return

    sourceIp.matches(IP_ADDR_PATTERN); } public boolean hasValidDestinationIp() { return destinationIp.matches(IP_ADDR_PATTERN); }
  4. SOLID Single responsibility principle public class Employee { public Money

    calculatePay() { } public void save() { } public String reportHours() { } }
  5. SOLID Open/closed principle “You should be able to extend the

    behaviour of a system without having to modify that system.”
  6. SOLID Open/closed principle public class EmployeeRepository { private Logger logger

    = new Logger(); public void addEmployee(Employee employee) { create(employee); logger.employee_added(employee); } }
  7. SOLID Open/closed principle public class EmployeeRepository { private List<Listener> listeners

    = new ArrayList<Listener>(); public void addListener(Listener listener) { listeners.add(listener); } // ... }
  8. SOLID Open/closed principle public class EmployeeRepository { // ... public

    void hire(Employee employee) { create(employee); for (Listener listener : listeners) { listener.employee_added(employee); } } }
  9. SOLID Liskov substitution principle “Objects in a program should be

    replaceable with instances of their subtypes without altering the correctness of that program.”
  10. SOLID Liskov substitution principle public class Rectangle { public void

    setWidth(int width) { } public void setHeight(int height) { } } public class Square extends Rectangle { }
  11. SOLID Liskov substitution principle public void stretch(Rectangle rectangle) { rectangle.setWidth(2

    * rectangle.getWidth()); } Square square = Square.new(2); stretch(square); square.getHeight(); // 2 square.getWidth(); // 4
  12. SOLID Liskov substitution principle public void stretch(Rectangle rectangle) { rectangle.setWidth(2

    * rectangle.getWidth()); } Square square = Square.new(2); stretch(square); square.getHeight(); // 2 square.getWidth(); // 4
  13. SOLID Liskov substitution principle public void stretch(Rectangle rectangle) { rectangle.setWidth(2

    * rectangle.getWidth()); } Square square = Square.new(2); stretch(square); square.getHeight(); // 2 square.getWidth(); // 4
  14. SOLID Liskov substitution principle public class Rectangle { public void

    setWidth(int width) { } public void setHeight(int height) { } } public class Square { public void setSize(int size) { } }
  15. SOLID Interface segregation principle public class Employee { public void

    givePayrise(Money amount) { } public String getEmailAddress() { } } public class Newsletter { public void send(List<Employee> recipients) { } }
  16. SOLID Interface segregation principle public interface PaidEmployee { public void

    givePayrise(Money amount) { } public interface Contactable { public String getEmailAddress() { } public class Employee implements PaidEmployee, Contactable { }
  17. SOLID Interface segregation principle public interface PaidEmployee { public void

    givePayrise(Money amount) { } public interface Contactable { public String getEmailAddress() { } public class Employee implements PaidEmployee, Contactable { }
  18. SOLID Dependency inversion principle “(1) High-level modules should not depend

    on low-level modules. Both should depend on abstractions.”
  19. SOLID Dependency inversion principle “(2) Abstractions should not depend on

    details. Details should depend on abstractions.”
  20. SOLID Dependency inversion principle public class EmployeeRegistry { public void

    save(Employee employee) { db = Database.getConnection(); db.save("employees", employee); } }
  21. SOLID Dependency inversion principle public interface DbConnection; public class EmployeeRegistry

    { private dbConnection db; public EmployeeRegistry(DbConnection db) { this.db = db; } public void save(Employee employee) { db.save("employees", employee); } }
  22. Simple code: 1. Passes all its tests 2. Clearly expresses

    intent 3. Contains no duplication 4. Has no superfluous parts
  23. Simple code: 1. Passes all its tests 2. Clearly expresses

    intent 3. Contains no duplication 4. Has no superfluous parts