Slide 1

Slide 1 text

Clean Code Suraj Shirvankar Implementing SOLID principles in any programming language

Slide 2

Slide 2 text

2 WARNING

Slide 3

Slide 3 text

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.

Slide 4

Slide 4 text

4

Slide 5

Slide 5 text

5

Slide 6

Slide 6 text

6

Slide 7

Slide 7 text

7

Slide 8

Slide 8 text

CLEAN CODE Clean code is code that is easy to understand and easy to change. 8

Slide 9

Slide 9 text

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.

Slide 10

Slide 10 text

1 0

Slide 11

Slide 11 text

1 1

Slide 12

Slide 12 text

1 2 S.O.L.I.D PRINCIPLES

Slide 13

Slide 13 text

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.

Slide 14

Slide 14 text

1 4

Slide 15

Slide 15 text

Single Responsibility Principle • A class must do one thing and one thing only • Break bigger classes into smaller ones 1 5

Slide 16

Slide 16 text

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

Slide 17

Slide 17 text

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.

Slide 18

Slide 18 text

1 8

Slide 19

Slide 19 text

Open Close Principle • You should allow users to add new functionalities without changing existing code. 1 9

Slide 20

Slide 20 text

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 }

Slide 21

Slide 21 text

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')

Slide 22

Slide 22 text

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

Slide 23

Slide 23 text

2 3

Slide 24

Slide 24 text

Liskov Substitution Principle • Derived types must be completely substitutable for their base types. 2 4

Slide 25

Slide 25 text

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);

Slide 26

Slide 26 text

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

Slide 27

Slide 27 text

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

Slide 28

Slide 28 text

2 8

Slide 29

Slide 29 text

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

Slide 30

Slide 30 text

3 0

Slide 31

Slide 31 text

3 1

Slide 32

Slide 32 text

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"); } }

Slide 33

Slide 33 text

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"); } }

Slide 34

Slide 34 text

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

Slide 35

Slide 35 text

3 5

Slide 36

Slide 36 text

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

Slide 37

Slide 37 text

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

Slide 38

Slide 38 text

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

Slide 39

Slide 39 text

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

Slide 40

Slide 40 text

4 0

Slide 41

Slide 41 text

SIMPLE RULES TO FOLLOW •Small functions •Meaningful names •No Comments •Simple classes •Refactor often •Prefer explicit over implicit dependency 4 1

Slide 42

Slide 42 text

BAD const yyyymmdstr = moment().format('YYYY/MM/DD'); 4 2 GOOD const currentDate = moment().format('YYYY/MM/DD'); Method Names

Slide 43

Slide 43 text

BAD setTimeout(blastOff, 86400000); 4 3 GOOD const MILLISECONDS_IN_A_DAY = 86400000; setTimeout(blastOff, MILLISECONDS_IN_A_DAY); DEFINE GOOD CONSTANTS

Slide 44

Slide 44 text

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

Slide 45

Slide 45 text

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

Slide 46

Slide 46 text

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

Slide 47

Slide 47 text

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

Slide 48

Slide 48 text

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

Slide 49

Slide 49 text

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

Slide 50

Slide 50 text

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

Slide 51

Slide 51 text

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

Slide 52

Slide 52 text

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)

Slide 53

Slide 53 text

function getMemes() { return [‘meme1.jpg’,‘meme2.jpg’,‘meme3.jpg’,‘meme4.jpg’,‘meme5.jpg’] } 5 3

Slide 54

Slide 54 text

5 4

Slide 55

Slide 55 text

//Returns 5 best memes function getMemes() { return [‘meme1.jpg’,‘meme2.jpg’,‘meme3.jpg’,‘meme4.jpg’,‘meme5.jpg’] } 5 5

Slide 56

Slide 56 text

5 6

Slide 57

Slide 57 text

//Returns 5 best memes function getMemes() { return [‘meme1.jpg’,‘meme2.jpg’,‘meme3.jpg’,‘meme4.jpg’,‘meme5.jpg’,‘meme6.jpg’,‘ meme7.jpg’,‘meme8.jpg’,‘meme9.jpg’,‘meme10.jpg’,‘meme11.jpg’,‘meme12.jpg’] } 5 7 OH WAIT BUT THE COMMENTS SAY THE METHOD RETURNS 5 Memes

Slide 58

Slide 58 text

5 8

Slide 59

Slide 59 text

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

Slide 60

Slide 60 text

6 0

Slide 61

Slide 61 text

6 1

Slide 62

Slide 62 text

Test All the things • Following clean code principles, makes testing a lot easier 6 2

Slide 63

Slide 63 text

WAKE UP 6 3

Slide 64

Slide 64 text

6 4

Slide 65

Slide 65 text

REFERENCES • http://blog.cleancoder.com/ • https://github.com/ryanmcdermott/clean-code-javascript 6 5