Slide 1

Slide 1 text

S.O.L.I.D. PRINCIPLES Ubiratan Soares October / 2018

Slide 2

Slide 2 text

No content

Slide 3

Slide 3 text

ROBERT MARTIN "Uncle Bob"

Slide 4

Slide 4 text

No content

Slide 5

Slide 5 text

S ingle Responsability O pen-Closed L iskov Substitution I nterface Segregation D ependency Inversion

Slide 6

Slide 6 text

OBJECT
 ORIENTATION

Slide 7

Slide 7 text

public class Animal { public void walk() { … } public void talk() { … } }

Slide 8

Slide 8 text

public class Human extends Animal { @Override public void walk() { super.walk(); !" TODO } @Override public void talk() { super.talk(); !" TODO } }

Slide 9

Slide 9 text

(new Human() instanceof Animal) // Always true Animal Human - walk( ) - talk( ) - walk( ) - talk( ) INHERITANCE

Slide 10

Slide 10 text

public class Engine { public void start() { !" TODO } }

Slide 11

Slide 11 text

public class Car { final Engine engine; public Car(Engine e) { engine = e; } public void turnOn() { engine.start(); // TODO } }

Slide 12

Slide 12 text

public class Car { final Engine engine; public Car(Engine e) { engine = e; } public void turnOn() { engine.start(); // TODO } }

Slide 13

Slide 13 text

COMPOSITION (new Car() instanceof Engine) // Always false Engine Car - start( ) - turnOn( )

Slide 14

Slide 14 text

"Dont call us, we call you" The Hollywood Principle

Slide 15

Slide 15 text

Engine Car - start( ) - turnOn( ) Animal Human - walk( ) - talk( ) - walk( ) - talk( ) Implements Extends Depends On

Slide 16

Slide 16 text

ABSTRACTIONS

Slide 17

Slide 17 text

public abstract class Validation { public abstract boolean validate(String input); }

Slide 18

Slide 18 text

public class NameValidator extends Validation { @Override public boolean validate(String input) { return input #$ null %& input.length() '( 50; } }

Slide 19

Slide 19 text

(new NameValidator() instanceof Validation) // Always true Validation NameValidator - validate(input) ABSTRACT CLASSES - validate(input)

Slide 20

Slide 20 text

public interface CheckSession { boolean isActive(); }

Slide 21

Slide 21 text

public class CookieChecker implements CheckSession { @Override public boolean isActive() { return true; } }

Slide 22

Slide 22 text

(new CookieChecker() instanceof CheckSession) // Always true CookieChecker INTERFACES - isActive( ) CheckSession - isActive( )

Slide 23

Slide 23 text

S.O.L.I.D.

Slide 24

Slide 24 text

Single Responsability Principle

Slide 25

Slide 25 text

TL;DR • Single Responsability Principle - SRP - is related with the semantics and API design of functional units of a software systems • The best functional units - functions, classes or even modules - are those ones that are built in order to accomplish just one well-defined responsability inside the system

Slide 26

Slide 26 text

Employee - calculatePayoff( ) - saveInfo( ) - describeEmployee( ) How many responsability does this class has? Answer : 3 responsabilities Salary Calculation Information Persistence Reporting

Slide 27

Slide 27 text

Employee - calculatePayoff( ) - saveInfo( ) - describeEmployee( ) And we if we add another method ? - findById( ) - reportWorkedHours( ) And we if we add another method ?

Slide 28

Slide 28 text

The responsability provided by some class is related with the clients interested on this class

Slide 29

Slide 29 text

HR Ops DBA Employee - calculatePayoff( ) - saveInfo( ) - describeEmployee( ) Report System Account System Ops System Devs + Users Devs + Users Devs + Users

Slide 30

Slide 30 text

function( ) function( ) function( ) function( ) function( ) function( ) function( ) function( ) HR Ops DBA Devs + Users Devs + Users Devs + Users

Slide 31

Slide 31 text

RESPONSABILITY A group of functions / behaviours that serve some system actor uniquely

Slide 32

Slide 32 text

• FANOUT = 3 • Employee is a FAT class • Responsability co-location means actor coupling • Fragility symptom Employee calculatePayoff( ) save( ) describeEmployee( ) ORM API Payoff API Reporter API CLIENT CLASS CLIENT CLASS

Slide 33

Slide 33 text

SINGLE RESPONSABILITY Each class / module should have one, and only one responsability inside our systems

Slide 34

Slide 34 text

SRP VIOLATION An Activity talk directly with database

Slide 35

Slide 35 text

SRP VIOLATION Account balance is calculated inside a store procedure

Slide 36

Slide 36 text

SRP VIOLATION Your Fragment call the desired REST API directly

Slide 37

Slide 37 text

SRP VIOLATION Your Fragment has state filled by GraphQL json

Slide 38

Slide 38 text

SRP VIOLATION Singletons instances are retrieved inside local method

Slide 39

Slide 39 text

SRP VIOLATION Static Logging

Slide 40

Slide 40 text

TIPS TO CONFORM WITH SRP • Identity actors • More small classes and more colaborators per class • Small functions / methods per class / module • Avoid statics as hell • Design Patterns

Slide 41

Slide 41 text

"NO MERCY !!! NO PRISIONERS!!! ALL YOUR CLASSES MUST FOLLOW THE S.R.P !!!"

Slide 42

Slide 42 text

S.R.P. One reason to change per class Side-affects visibility One actor served per class

Slide 43

Slide 43 text

Dependency Inversion Principle

Slide 44

Slide 44 text

TL;DR • Dependency Inversion Principle - DIP - is related with the impact of API design into the flow of control of program • Dependency Inversion is achieved when the API design enable decoupling of source-code dependencies from runtime dependencies

Slide 45

Slide 45 text

Engine Car - start( ) - turnOn( ) javac Engine.class Car.class JVM (Runtime)

Slide 46

Slide 46 text

Engine.java Car.java - start( ) - turnOn( ) • A source-code dependency • The name Engine appears at Car.java file

Slide 47

Slide 47 text

Engine.class Car.class JVM (Runtime) • A runtime dependency • In order to execute this program, the JVM must correlate instances with classes scanned into classpath, and Car instance will require a Engine instance

Slide 48

Slide 48 text

Usually in static typed languages, the hierarchical code design will enforce that runtime dependencies mirror source-code dependencies

Slide 49

Slide 49 text

But …

Slide 50

Slide 50 text

Engine Car - start( flags) - turnOn( ) javac Engine.class Car.class JVM (Runtime)

Slide 51

Slide 51 text

Engine Car - start( flags) - turnOn( ) Engine.class Car.class javac FuelTank - capacity( ) FuelTank.class JVM (Runtime)

Slide 52

Slide 52 text

App A B C F E K N M L O G J H I D

Slide 53

Slide 53 text

App A B C F E K N M L O G* J H I D Recompilation cascade Re-deploying strategy ?

Slide 54

Slide 54 text

Can this be different?

Slide 55

Slide 55 text

Engine.java Car.java - start( ) - turnOn( )

Slide 56

Slide 56 text

Engine.java Car.java - start( ) - turnOn( ) GasolineEngine.java - start( )

Slide 57

Slide 57 text

public interface Engine { public void start(boolean flag); } public class GasolineEngine implements Engine { @Override public void start(boolean flag) { !" TODO } } public class Car { final Engine engine; public Car(Engine e) { engine = e; } public void turnOn() { engine.start(false); } }

Slide 58

Slide 58 text

public class App { public static void main(String[] args) { Engine gasolinePowered = new GasolineEngine(); Car mercedes = new Car(gasolinePowered); mercedes.turnOn(); } }

Slide 59

Slide 59 text

public class App { public static void main(String[] args) { Engine gasolinePowered = new GasolineEngine(); Car mercedes = new Car(gasolinePowered); mercedes.turnOn(); } }

Slide 60

Slide 60 text

Engine.java Car.java - start( ) - turnOn( ) GasolinEngine.java - start( ) javac GasolineEngine.java Car.class JVM (Runtime)

Slide 61

Slide 61 text

Engine.java Car.java - start( ) - turnOn( ) GasolineEngine.java - start( ) GasolineEngine.java Car.class JVM (Runtime)

Slide 62

Slide 62 text

The source code dependency from Car to GasolineEngine has the oposite direction comparated to the related runtime dependency

Slide 63

Slide 63 text

BEHOLD THE DEPENDENCY INVERSION

Slide 64

Slide 64 text

Engine.java Car.java - start( ) - turnOn( ) GasolineEngine.java - start( ) Car-v0.1.jar Engine-v0.1.jar BUILD SYSTEM

Slide 65

Slide 65 text

Engine.java Car.java - start( ) - turnOn( ) EletricEngine.java - start( ) Car-v0.1.jar Engine-v0.2.jar BUILD SYSTEM

Slide 66

Slide 66 text

Engine.java Car.java - start( ) - turnOn( ) EletricEngine.java - start( ) Car-v0.1.jar Engine-v0.2.jar BUILD SYSTEM Recompiled Re-deployed

Slide 67

Slide 67 text

Car-v0.1.jar Engine-v0.2.jar JVM (Runtime) Engine becames a plugin to the Car application

Slide 68

Slide 68 text

D.I.P. Source-code dependencies decoupling from runtime dependencies Enables plugin-like API design The art of indirections

Slide 69

Slide 69 text

Interfaces Segregation Principle

Slide 70

Slide 70 text

TL;DR • The Interface Segregation Principle - ISP - is a practical consideration for your interfaces design • Your interfaces must not leak abstractions : ie, clients of some behaviour must not know things they don't need

Slide 71

Slide 71 text

Payment Order - name( ) CreditcardPayment - name( ) - processor( ) - processor( )

Slide 72

Slide 72 text

Payment Order - name( ) CreditcardPayment - name( ) - processor( ) - processor( ) BankslipPayment - name( ) - processor( ) CashPayment - name( ) - processor( )

Slide 73

Slide 73 text

Several features later …

Slide 74

Slide 74 text

Payment Order - name( ) CreditcardPayment - fee( ) BankslipPayment - name( ) - fee( ) CashPayment - parcels( ) - discount( ) - parcels( ) - … - name( ) - fee( ) - … - name( ) - fee( ) - … - processor( ) - cashback( ) - isApproved( ) Promotions …

Slide 75

Slide 75 text

Payment - name( ) - fee( ) CashPayment - parcels( ) - discount( ) - parcels( ) - processor( ) - cashback( ) - isApproved( ) - name( ) - fee( ) - parcels( ) - discount( ) - parcels( ) - processor( ) - cashback( ) - isApproved( )

Slide 76

Slide 76 text

Sometimes when leveraging on interfaces, it is easy to get implementations bloated with "un-needed" methods, and suffer the same issues from FAT classes

Slide 77

Slide 77 text

LEAKY ABSTRACTIONS IN PRACTICE

Slide 78

Slide 78 text

How can we enable a new feature to some payment method - required by the Order - without leak this abstraction to other payment methods ???

Slide 79

Slide 79 text

Rely on the most relevant interface feature !!!

Slide 80

Slide 80 text

A B C

Slide 81

Slide 81 text

BankslipPayment SomeOrder Description ThirdPartyHandled Splittable Approvable BenefitsGranted - name( ) - fee( ) - discount( ) - processor( ) - isApproved( )

Slide 82

Slide 82 text

CashPayment Another Description BenefitsGranted - name( ) - discount( ) - processor( )

Slide 83

Slide 83 text

I.S.P. Leverage class composition and/or interface inheritance + default methods in order to let the clients of some abstraction know only exactly what they need to know, nothing more

Slide 84

Slide 84 text

Lyskov Substitution Principle

Slide 85

Slide 85 text

TL;DR • Liskov Substitution Principle - LSP - is a practical consideration to effective subtypes construction • A good OO software must ensure that all open types are perfectably substitutable : i.e. you may expect the exact same behaviour from a supertype or a subtype

Slide 86

Slide 86 text

TYPES

Slide 87

Slide 87 text

Integer Binary representation with C2 or C1 ?? LSB or MSB? 32-bit or 64-bit ? Signed or Unsigned

Slide 88

Slide 88 text

What can I do with an Integer, anyway ?

Slide 89

Slide 89 text

TYPE Public behaviour (API) Hidden implementations Hidden data Supported operations

Slide 90

Slide 90 text

typedef struct node { int val; struct node * next; } node_t; void print_list(node_t * head); void push(node_t * head, int val); void push(node_t ** head, int val); int pop(node_t ** head); int remove_last(node_t * head); int remove_by_index(node_t ** head, int n); ------------------------------ ------------------------------ | | | \ | | | | DATA | NEXT |--------------| DATA | NEXT | | | | / | | | ------------------------------ ------------------------------

Slide 91

Slide 91 text

TYPES In OO languages, types are usually defined by classes or interfaces

Slide 92

Slide 92 text

SUBTYPE Class or interface that extends some type

Slide 93

Slide 93 text

TYPE SAFETY

Slide 94

Slide 94 text

public class Duck implements Animal { public String speak() { return "Quack, quack!"; } public String move() { return "Flap, flap"; } } public class Person implements Animal { public String speak() { return "Hi"; } public String move() { return “I`m coding now …”; } } public interface Animal { String speak(); String move(); }

Slide 95

Slide 95 text

public class Main { public static void main(String[] args) { poke(new Duck()); poke(new Person()); } static void poke(Animal pet) { System.out.println(pet.speak()); System.out.println(pet.move()); } } > java -jar sample.jar Quack, quack! Flap, flap Hello! I`m coding now ...

Slide 96

Slide 96 text

class Duck: def quack(self): return "Quack, quack!" def fly(self): return "Flap, Flap!" class Person: def quack(self): return "I'm Quackin'!" def fly(self): return "I'm Flyin'!" def in_the_forest(animal): print(animal.quack()) print(animal.fly()) in_the_forest(Duck()) in_the_forest(Person()) > python sample.py Quack, quack! Flap, Flap! I'm Quackin'! I'm Flyin'!

Slide 97

Slide 97 text

Do I have a criteria in order to not make a huge mess while adding new behavior to a existing type ???

Slide 98

Slide 98 text

"If for each object o1 of type S there is an object o2 of type T such that for all programs P defined in terms of T, the behavior of P is unchanged when o1 is substituted for o2 then S is a subtype of T."

Slide 99

Slide 99 text

P A B For P, does not matter if the colaborator instance is from type A or B !!!!

Slide 100

Slide 100 text

THE REFUSED BEQUEST • Design smell related to LSP violation. • Proposed by Martin Fowler (Refactoring) • Happens when the subtype "does not care" about supertype methods and overwrite them improperly • Typical scenarios : side-effects, throwed exceptions, etc

Slide 101

Slide 101 text

public class Rectangle { int width, height; public int perimeter() { return 2 * (width + height); } public int area() { return width * height; } public void setHeight(int height) { this.height = height; } public void setWidth(int width) { this.width = width; } public int getWidth() { return width; } public int getHeight() { return height; } }

Slide 102

Slide 102 text

public class Rectangle { int width, height; public int perimeter() { return 2 * (width + height); } public int area() { return width * height; } public void setHeight(int height) { this.height = height; } public void setWidth(int width) { this.width = width; } public int getWidth() { return width; } public int getHeight() { return height; } }

Slide 103

Slide 103 text

public class Square extends Rectangle { @Override public void setHeight(int height) { super.setHeight(height); } @Override public void setWidth(int width) { super.setWidth(width); setHeight(width); } }

Slide 104

Slide 104 text

public class Square extends Rectangle { @Override public void setHeight(int height) { super.setHeight(height); } @Override public void setWidth(int width) { super.setWidth(width); setHeight(width); } }

Slide 105

Slide 105 text

public static void main(String[] args) { setupAndPrint(new Rectangle()); setupAndPrint(new Square()); } static void setupAndPrint(Rectangle r) { r.setHeight(2); r.setWidth(10); System.out.println(r.area()); }

Slide 106

Slide 106 text

public static void main(String[] args) { setupAndPrint(new Rectangle()); setupAndPrint(new Square()); } static void setupAndPrint(Rectangle r) { r.setHeight(2); r.setWidth(10); System.out.println(r.area()); }

Slide 107

Slide 107 text

public static void main(String[] args) { setupAndPrint(new Rectangle()); setupAndPrint(new Square()); } static void setupAndPrint(Rectangle r) { r.setHeight(2); r.setWidth(10); System.out.println(r.area()); } > java -jar app.jar 20 100

Slide 108

Slide 108 text

public static void main(String[] args) { setupAndPrint(new Rectangle()); setupAndPrint(new Square()); } static void setupAndPrint(Rectangle r) { r.setHeight(2); r.setWidth(10); System.out.println(r.area()); } > java -jar app.jar 20 100

Slide 109

Slide 109 text

Can this be solved ??? But with a terrible side-effect, because the client of Rectangle abstraction now knows about low level details (Square) … YES : instanceOf

Slide 110

Slide 110 text

THE RIGHT WAY Square and Rectangle should be designed without any type- relation inside this system !!!!

Slide 111

Slide 111 text

"In a software system, abstractions may not preserve the same relationships that they related concepts have in the real world" Representative Rule

Slide 112

Slide 112 text

Real Complex Integer 2 Is this Math relation between numbers possible in software, compiler ???

Slide 113

Slide 113 text

Real Complex Integer 2 Is this Math relation between numbers possible in software, compiler ???

Slide 114

Slide 114 text

L.S.P. Remember the Representative Rule Avoid instanceOf as hell Subtypes must be perfectably substitutable Design your subtypes wisely

Slide 115

Slide 115 text

Open Closed Principle

Slide 116

Slide 116 text

TL;DR • The Open-closed Principle - OCP - is the moral center of a software system • Ideally, a software system should be designed in a way that it is open for extensions but closed for modifications

Slide 117

Slide 117 text

No content

Slide 118

Slide 118 text

Open to extensions : means that we can add new features to the system easily Closed to modification : means that we can achieve this without source-code changes

Slide 119

Slide 119 text

D.I.P. and L.S.P. are keys to design a system in a way that high level policies (behaviours) do not depend directly on low level details (implementations)

Slide 120

Slide 120 text

THE EXPECTATION The O.C.P. concept reflects the real vision of users and/or business for a software project

Slide 121

Slide 121 text

Users Business Developers Product

Slide 122

Slide 122 text

How can we deal with software entropy ?

Slide 123

Slide 123 text

BIG DESIGN UP-FRONT AGILE DESING • Effective to small scopes • Not scalable to great projects • Over-engineering • No scope defined • Retrospective learning • Incremental explorations over domain • Adaptation and re-working

Slide 124

Slide 124 text

O.C.P. Cannot be 100% achieved in practice, but must be seeked All other 4 principles violations + bad code are factors that compromise the OCP The moral center of software Reflects the entropy control

Slide 125

Slide 125 text

Feelings Report !!!

Slide 126

Slide 126 text

No content

Slide 127

Slide 127 text

No content

Slide 128

Slide 128 text

https://speakerdeck.com/ubiratansoares

Slide 129

Slide 129 text

UBIRATAN SOARES Computer Scientist by ICMC/USP Software Engineer, curious guy Google Developer Expert Teacher, speaker, etc, etc

Slide 130

Slide 130 text

THANK YOU @ubiratanfsoares ubiratansoares.github.io https://br.linkedin.com/in/ubiratanfsoares