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

Clean Code - Implementing SOLID principles in any programming language

Clean Code - Implementing SOLID principles in any programming language

Suraj Shirvankar

May 31, 2017
Tweet

More Decks by Suraj Shirvankar

Other Decks in Programming

Transcript

  1. Suraj Shirvankar 3 @h0lyalg0rithm surajms.com Suraj Shirvankar was raised in

    Dubai. He is obsessed with learning different languages like Ruby, Java, Elixir and JavaScript. After some experience in a startup accelerator, he decided to move from Dubai to Spain where he works as the CTO of Paack.
  2. 4

  3. 5

  4. 6

  5. 7

  6. CLEAN CODE Clean code is code that is easy to

    understand and easy to change. 8
  7. Uncle Bob 9 http://cleancoder.com Robert Cecil Martin (colloquially known as

    Uncle Bob) is an American software engineer and author. He is a co-author of the Agile Manifesto.
  8. 1 0

  9. 1 1

  10. 1 3 S.O.L.I.D PRINCIPLES SRP The Single Responsibility Principle A

    class should have one, and only one, reason to change. OCP The Open Closed Principle You should be able to extend a classes behavior, without modifying it. LSP The Liskov Substitution Principle Derived classes must be substitutable for their base classes. ISP The Interface Segregation Principle Make fine grained interfaces that are client specific. DIP The Dependency Inversion Principle Depend on abstractions, not on concretions.
  11. 1 4

  12. Single Responsibility Principle • A class must do one thing

    and one thing only • Break bigger classes into smaller ones 1 5
  13. BEFORE class UserSettings { constructor(user) { this.user = user; }

    changeSettings(settings) { if (this.verifyCredentials()) { // ... } } verifyCredentials() { // ... } } 1 6 AFTER class UserAuth { constructor(user) { this.user = user; } verifyCredentials() { } } class UserSettings { constructor(user) { this.user = user; this.auth = new UserAuth(user); } changeSettings(settings) { if (this.auth.verifyCredentials()) { } } } SRP
  14. 1 7 S.O.L.I.D PRINCIPLES SRP The Single Responsibility Principle A

    class should have one, and only one, reason to change. OCP The Open Closed Principle You should be able to extend a classes behavior, without modifying it. LSP The Liskov Substitution Principle Derived classes must be substitutable for their base classes. ISP The Interface Segregation Principle Make fine grained interfaces that are client specific. DIP The Dependency Inversion Principle Depend on abstractions, not on concretions.
  15. 1 8

  16. Open Close Principle • You should allow users to add

    new functionalities without changing existing code. 1 9
  17. class AjaxAdapter extends Adapter { constructor() { super(); this.name =

    'ajaxAdapter'; } } class LocalStorageAdapter extends Adapter { constructor() { super(); this.name = 'localStorageAdapter'; } } class DataRequester { constructor(adapter) { this.adapter = adapter; } fetch(url) { if (this.adapter.name === 'ajaxAdapter') { return makeAjaxCall(url).then((response) => { // transform response and return }); } else if(this.adapter.name === 'localStorageAdapter') { return makeLocalStorageCall(url).then((response) => { // transform response and return }); } } } 2 0 function makeAjaxCall(url) { // request and return promise } function makeLocalStorageCall(url) { // request and return promise }
  18. class AjaxAdapter extends Adapter { constructor() { super(); this.name =

    'ajaxAdapter'; } request(url) { // request and return promise } } class LocalStorageAdapter extends Adapter { constructor() { super(); this.name = 'localStorageAdapter'; } request(url) { // request and return promise } } class HttpRequester { constructor(adapter) { this.adapter = adapter; } fetch(url) { return this.adapter.request(url).then((response) => { // transform response and return }); } } 2 1 requester = new HttpRequester(ajaxAdapter) requester.fetch(‘http://example.com')
  19. 2 2 SRP The Single Responsibility Principle A class should

    have one, and only one, reason to change. OCP The Open Closed Principle You should be able to extend a classes behavior, without modifying it. LSP The Liskov Substitution Principle Derived classes must be substitutable for their base classes. ISP The Interface Segregation Principle Make fine grained interfaces that are client specific. DIP The Dependency Inversion Principle Depend on abstractions, not on concretions. S.O.L.I.D PRINCIPLES
  20. 2 3

  21. class Rectangle { constructor() { this.width = 0; this.height =

    0; } render(area) { } setWidth(width) { this.width = width; } setHeight(height) { this.height = height; } getArea() { return this.width * this.height; } } class Square extends Rectangle { setWidth(width) { this.width = width; this.height = width; } setHeight(height) { this.width = height; this.height = height; } } 2 5 function renderLargeRectangles(rectangles) { rectangles.forEach((rectangle) => { rectangle.setWidth(4); rectangle.setHeight(5); const area = rectangle.getArea(); // BAD: Returns 25 for Square. Should be 20. rectangle.render(area); }); } const rectangles = [new Rectangle(), new Rectangle(), new Square()]; renderLargeRectangles(rectangles);
  22. class Shape { render(area) { // ... } } class

    Rectangle extends Shape { constructor(width, height) { super(); this.width = width; this.height = height; } getArea() { return this.width * this.height; } } class Square extends Shape { constructor(length) { super(); this.length = length; } getArea() { return this.length * this.length; } } function renderLargeShapes(shapes) { shapes.forEach((shape) => { const area = shape.getArea(); shape.render(area); }); } const shapes = [new Rectangle(4, 5), new Rectangle(4, 5), new Square(5)]; renderLargeShapes(shapes); 2 6
  23. 2 7 SRP The Single Responsibility Principle A class should

    have one, and only one, reason to change. OCP The Open Closed Principle You should be able to extend a classes behavior, without modifying it. LSP The Liskov Substitution Principle Derived classes must be substitutable for their base classes. ISP The Interface Segregation Principle Make fine grained interfaces that are client specific. DIP The Dependency Inversion Principle Depend on abstractions, not on concretions. S.O.L.I.D PRINCIPLES
  24. 2 8

  25. Interface Segregation • A class should not be forced to

    depend on methods it does not use • Make fine grained interfaces that are client specific. 2 9
  26. 3 0

  27. 3 1

  28. public interface Machine { public void print(); public void staple();

    public void scan(); public void photoCopy(); } public class AllInOneMachine implements Machine { @Override public void print() { System.out.println("Printing Job"); } @Override public void staple() { System.out.println("Stapling Job"); } @Override public void scan() { System.out.println("Scan Job"); } @Override public void photoCopy() { System.out.println("Photo Copy"); } } 3 2 public class XeroxMachine implements Machine { @Override public void print() { System.out.println("Printing Job"); } @Override public void staple() { System.out.println("Stapling Job"); } @Override public void scan() { // Wait cannot scan its a xerox machine } @Override public void photoCopy() { System.out.println("Photo Copy"); } }
  29. 3 3 public interface Printer { public void print(); }

    public interface Scanner { public void fax(); } public interface Stapler { public void staple(); } public interface PhotoCopier { public void photoCopy(); } public class XeroxMachine implements Printer, PhotoCopier { @Override public void print() { System.out.println("Printing Job"); } @Override public void photoCopy() { System.out.println("Photo Copy"); } }
  30. 3 4 SRP The Single Responsibility Principle A class should

    have one, and only one, reason to change. OCP The Open Closed Principle You should be able to extend a classes behavior, without modifying it. LSP The Liskov Substitution Principle Derived classes must be substitutable for their base classes. ISP The Interface Segregation Principle Make fine grained interfaces that are client specific. DIP The Dependency Inversion Principle Depend on abstractions, not on concretions. S.O.L.I.D PRINCIPLES
  31. 3 5

  32. Dependency Inversion Principle • High-level modules should not depend on

    low-level modules. Both should depend on abstractions. • Abstractions should not depend upon details. Details should depend on abstractions. 3 6
  33. class InventoryRequester { constructor() { this.REQ_METHODS = ['HTTP']; } requestItem(item)

    { // ... } } class InventoryTracker { constructor(items) { this.items = items; // BAD: We have created a dependency on a specific request implementation. // We should just have requestItems depend on a request method: `request` this.requester = new InventoryRequester(); } requestItems() { this.items.forEach((item) => { this.requester.requestItem(item); }); } } const inventoryTracker = new InventoryTracker(['apples', 'bananas']); inventoryTracker.requestItems(); 3 7
  34. class InventoryTracker { constructor(items, requester) { this.items = items; this.requester

    = requester; } requestItems() { this.items.forEach((item) => { this.requester.requestItem(item); }); } } class InventoryRequesterV1 { constructor() { this.REQ_METHODS = ['HTTP']; } requestItem(item) { } } class InventoryRequesterV2 { constructor() { this.REQ_METHODS = ['WS']; } requestItem(item) { } } // By constructing our dependencies externally and injecting them, we can easily // substitute our request module for a fancy new one that uses WebSockets. const inventoryTracker = new InventoryTracker(['apples', 'bananas'], new InventoryRequesterV2()); inventoryTracker.requestItems(); 3 8
  35. 3 9 SRP The Single Responsibility Principle A class should

    have one, and only one, reason to change. OCP The Open Closed Principle You should be able to extend a classes behavior, without modifying it. LSP The Liskov Substitution Principle Derived classes must be substitutable for their base classes. ISP The Interface Segregation Principle Make fine grained interfaces that are client specific. DIP The Dependency Inversion Principle Depend on abstractions, not on concretions. S.O.L.I.D PRINCIPLES
  36. 4 0

  37. SIMPLE RULES TO FOLLOW •Small functions •Meaningful names •No Comments

    •Simple classes •Refactor often •Prefer explicit over implicit dependency 4 1
  38. BAD setTimeout(blastOff, 86400000); 4 3 GOOD const MILLISECONDS_IN_A_DAY = 86400000;

    setTimeout(blastOff, MILLISECONDS_IN_A_DAY); DEFINE GOOD CONSTANTS
  39. BAD const Car = { carMake: 'Honda', carModel: 'Accord', carColor:

    'Blue' }; function paintCar(car) { car.carColor = 'Red'; } 4 4 GOOD const Car = { make: 'Honda', model: 'Accord', color: 'Blue' }; function paintCar(car) { car.color = 'Red'; } DRY VARIABLE NAMES
  40. BAD function addToDate(date, month) { // ... } const date

    = new Date(); addToDate(date, 1); 4 5 GOOD function addMonthToDate(month, date) { // ... } const date = new Date(); addMonthToDate(1, date); GOOD METHOD NAMES
  41. BAD function createMenu(title, subtitle, body, buttonText, cancellable) { // ...

    } 4 6 GOOD function createMenu({ title, body, buttonText, cancellable }) { // ... } createMenu({ title: 'Foo', body: 'Bar', buttonText: 'Baz', cancellable: true }); FUNCTION ARGUMENTS
  42. BAD if (fsm.state === 'fetching' && isEmpty(listNode)) { // ...

    } 4 7 GOOD function shouldShowSpinner(fsm, listNode) { return fsm.state === 'fetching' && isEmpty(listNode); } if (shouldShowSpinner(fsmInstance, listNodeInstance)) { // ... } SIMPLER METHODS
  43. BAD function emailClients(clients) { clients.forEach((client) => { const clientRecord =

    database.lookup(client); if (clientRecord.isActive()) { email(client); } }); } 4 8 GOOD function emailActiveClients(clients) { clients .filter(isActiveClient) .forEach(email); } function isActiveClient(client) { const clientRecord = database.lookup(client); return clientRecord.isActive(); } SIMPLER METHODS
  44. BAD let name = 'Ryan McDermott'; function splitIntoFirstAndLastName() { name

    = name.split(' '); } splitIntoFirstAndLastName(); console.log(name); // ['Ryan', 'McDermott']; 4 9 GOOD function splitIntoFirstAndLastName(name) { return name.split(' '); } const name = 'Ryan McDermott'; const newName = splitIntoFirstAndLastName(name); console.log(name); // 'Ryan McDermott'; console.log(newName); // ['Ryan', 'McDermott']; REMOVE SIDE EFFECTS
  45. BAD function travelToBCN(vehicle) { if (vehicle instanceof Bicycle) { vehicle.pedal(this.currentLocation,

    this.cityCenter); } else if (vehicle instanceof Car) { vehicle.drive(this.currentLocation, this.cityCenter); } } 5 0 GOOD function travelToBCN(vehicle) { vehicle.move(this.currentLocation, this.cityCenter); } AVOID TYPE CHECKING
  46. BAD function oldRequestModule(url) { // ... } function newRequestModule(url) {

    // ... } const req = newRequestModule; inventoryTracker('apples', req, 'www.inventory-awesome.io'); 5 1 GOOD function newRequestModule(url) { // ... } const req = newRequestModule; inventoryTracker('apples', req, 'www.inventory-awesome.io'); REMOVE DEAD CODE
  47. BAD class Car { constructor() { this.make = 'Honda'; this.model

    = 'Accord'; this.color = 'white'; } setMake(make) { this.make = make; } setModel(model) { this.model = model; } setColor(color) { this.color = color; } save() { console.log(this.make, this.model, this.color); } } const car = new Car(); car.setColor('pink'); car.setMake('Ford'); car.setModel('F-150'); car.save(); 5 2 GOOD class CarBuilder { constructor() { this.make = 'Honda'; this.model = 'Accord'; this.color = 'white'; } setMake(make) { this.make = make; return this; } setModel(model) { this.model = model; return this; } setColor(color) { this.color = color; return this; } build() { console.log(this.make, this.model, this.color); return this; } } const car = new CarBuilder() .setColor('pink') .setMake('Ford') .setModel('F-150') .build(); USE METHOD CHAINING (BUILDER PATTERN)
  48. 5 4

  49. 5 6

  50. 5 8

  51. Refactor Code • Keep your code D.R.Y (Dont repeat yourself).

    • Extract common functionality from different places into a method or object. • Remove dead code 5 9
  52. 6 0

  53. 6 1

  54. 6 4