SOLID Principles

SOLID Principles

Presentation / class about S.O.L.I.D. principles by Uncle Bob Martin.

Given at the following events

- Semana da Computação Mackenzie (October / 2018)
- AndroidFest by GDGCampinas / AndroidDevBR (October/2018)

D4b7a3e2ed10f86e0b52498713ba2601?s=128

Ubiratan Soares

October 02, 2018
Tweet

Transcript

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

  2. None
  3. ROBERT MARTIN "Uncle Bob"

  4. None
  5. S ingle Responsability O pen-Closed L iskov Substitution I nterface

    Segregation D ependency Inversion
  6. OBJECT
 ORIENTATION

  7. public class Animal { public void walk() { … }

    public void talk() { … } }
  8. public class Human extends Animal { @Override public void walk()

    { super.walk(); !" TODO } @Override public void talk() { super.talk(); !" TODO } }
  9. (new Human() instanceof Animal) // Always true Animal Human -

    walk( ) - talk( ) - walk( ) - talk( ) INHERITANCE
  10. public class Engine { public void start() { !" TODO

    } }
  11. public class Car { final Engine engine; public Car(Engine e)

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

    { engine = e; } public void turnOn() { engine.start(); // TODO } }
  13. COMPOSITION (new Car() instanceof Engine) // Always false Engine Car

    - start( ) - turnOn( )
  14. "Dont call us, we call you" The Hollywood Principle

  15. Engine Car - start( ) - turnOn( ) Animal Human

    - walk( ) - talk( ) - walk( ) - talk( ) Implements Extends Depends On
  16. ABSTRACTIONS

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

    }
  18. public class NameValidator extends Validation { @Override public boolean validate(String

    input) { return input #$ null %& input.length() '( 50; } }
  19. (new NameValidator() instanceof Validation) // Always true Validation NameValidator -

    validate(input) ABSTRACT CLASSES - validate(input)
  20. public interface CheckSession { boolean isActive(); }

  21. public class CookieChecker implements CheckSession { @Override public boolean isActive()

    { return true; } }
  22. (new CookieChecker() instanceof CheckSession) // Always true CookieChecker INTERFACES -

    isActive( ) CheckSession - isActive( )
  23. S.O.L.I.D.

  24. Single Responsability Principle

  25. 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
  26. Employee - calculatePayoff( ) - saveInfo( ) - describeEmployee( )

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

    And we if we add another method ? - findById( ) - reportWorkedHours( ) And we if we add another method ?
  28. The responsability provided by some class is related with the

    clients interested on this class
  29. HR Ops DBA Employee - calculatePayoff( ) - saveInfo( )

    - describeEmployee( ) Report System Account System Ops System Devs + Users Devs + Users Devs + Users
  30. function( ) function( ) function( ) function( ) function( )

    function( ) function( ) function( ) HR Ops DBA Devs + Users Devs + Users Devs + Users
  31. RESPONSABILITY A group of functions / behaviours that serve some

    system actor uniquely
  32. • 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
  33. SINGLE RESPONSABILITY Each class / module should have one, and

    only one responsability inside our systems
  34. SRP VIOLATION An Activity talk directly with database

  35. SRP VIOLATION Account balance is calculated inside a store procedure

  36. SRP VIOLATION Your Fragment call the desired REST API directly

  37. SRP VIOLATION Your Fragment has state filled by GraphQL json

  38. SRP VIOLATION Singletons instances are retrieved inside local method

  39. SRP VIOLATION Static Logging

  40. 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
  41. "NO MERCY !!! NO PRISIONERS!!! ALL YOUR CLASSES MUST FOLLOW

    THE S.R.P !!!"
  42. S.R.P. One reason to change per class Side-affects visibility One

    actor served per class
  43. Dependency Inversion Principle

  44. 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
  45. Engine Car - start( ) - turnOn( ) javac Engine.class

    Car.class JVM (Runtime)
  46. Engine.java Car.java - start( ) - turnOn( ) • A

    source-code dependency • The name Engine appears at Car.java file
  47. 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
  48. Usually in static typed languages, the hierarchical code design will

    enforce that runtime dependencies mirror source-code dependencies
  49. But …

  50. Engine Car - start( flags) - turnOn( ) javac Engine.class

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

    javac FuelTank - capacity( ) FuelTank.class JVM (Runtime)
  52. App A B C F E K N M L

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

    O G* J H I D Recompilation cascade Re-deploying strategy ?
  54. Can this be different?

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

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

    start( )
  57. 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); } }
  58. public class App { public static void main(String[] args) {

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

    Engine gasolinePowered = new GasolineEngine(); Car mercedes = new Car(gasolinePowered); mercedes.turnOn(); } }
  60. Engine.java Car.java - start( ) - turnOn( ) GasolinEngine.java -

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

    start( ) GasolineEngine.java Car.class JVM (Runtime)
  62. The source code dependency from Car to GasolineEngine has the

    oposite direction comparated to the related runtime dependency
  63. BEHOLD THE DEPENDENCY INVERSION

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

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

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

    start( ) Car-v0.1.jar Engine-v0.2.jar BUILD SYSTEM Recompiled Re-deployed
  67. Car-v0.1.jar Engine-v0.2.jar JVM (Runtime) Engine becames a plugin to the

    Car application
  68. D.I.P. Source-code dependencies decoupling from runtime dependencies Enables plugin-like API

    design The art of indirections
  69. Interfaces Segregation Principle

  70. 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
  71. Payment Order - name( ) CreditcardPayment - name( ) -

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

    processor( ) - processor( ) BankslipPayment - name( ) - processor( ) CashPayment - name( ) - processor( )
  73. Several features later …

  74. Payment Order - name( ) CreditcardPayment - fee( ) BankslipPayment

    - name( ) - fee( ) CashPayment - parcels( ) - discount( ) - parcels( ) - … - name( ) - fee( ) - … - name( ) - fee( ) - … - processor( ) - cashback( ) - isApproved( ) Promotions …
  75. Payment - name( ) - fee( ) CashPayment - parcels(

    ) - discount( ) - parcels( ) - processor( ) - cashback( ) - isApproved( ) - name( ) - fee( ) - parcels( ) - discount( ) - parcels( ) - processor( ) - cashback( ) - isApproved( )
  76. Sometimes when leveraging on interfaces, it is easy to get

    implementations bloated with "un-needed" methods, and suffer the same issues from FAT classes
  77. LEAKY ABSTRACTIONS IN PRACTICE

  78. How can we enable a new feature to some payment

    method - required by the Order - without leak this abstraction to other payment methods ???
  79. Rely on the most relevant interface feature !!!

  80. A B C

  81. BankslipPayment SomeOrder Description ThirdPartyHandled Splittable Approvable BenefitsGranted - name( )

    - fee( ) - discount( ) - processor( ) - isApproved( )
  82. CashPayment Another Description BenefitsGranted - name( ) - discount( )

    - processor( )
  83. 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
  84. Lyskov Substitution Principle

  85. 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
  86. TYPES

  87. Integer Binary representation with C2 or C1 ?? LSB or

    MSB? 32-bit or 64-bit ? Signed or Unsigned
  88. What can I do with an Integer, anyway ?

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

  90. 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 | | | | / | | | ------------------------------ ------------------------------
  91. TYPES In OO languages, types are usually defined by classes

    or interfaces
  92. SUBTYPE Class or interface that extends some type

  93. TYPE SAFETY

  94. 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(); }
  95. 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 ...
  96. 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'!
  97. Do I have a criteria in order to not make

    a huge mess while adding new behavior to a existing type ???
  98. "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."
  99. P A B For P, does not matter if the

    colaborator instance is from type A or B !!!!
  100. 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
  101. 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; } }
  102. 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; } }
  103. 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); } }
  104. 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); } }
  105. 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()); }
  106. 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()); }
  107. 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
  108. 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
  109. 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
  110. THE RIGHT WAY Square and Rectangle should be designed without

    any type- relation inside this system !!!!
  111. "In a software system, abstractions may not preserve the same

    relationships that they related concepts have in the real world" Representative Rule
  112. Real Complex Integer 2 Is this Math relation between numbers

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

    possible in software, compiler ???
  114. L.S.P. Remember the Representative Rule Avoid instanceOf as hell Subtypes

    must be perfectably substitutable Design your subtypes wisely
  115. Open Closed Principle

  116. 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
  117. None
  118. 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
  119. 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)
  120. THE EXPECTATION The O.C.P. concept reflects the real vision of

    users and/or business for a software project
  121. Users Business Developers Product

  122. How can we deal with software entropy ?

  123. 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
  124. 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
  125. Feelings Report !!!

  126. None
  127. None
  128. https://speakerdeck.com/ubiratansoares

  129. UBIRATAN SOARES Computer Scientist by ICMC/USP Software Engineer, curious guy

    Google Developer Expert Teacher, speaker, etc, etc
  130. THANK YOU @ubiratanfsoares ubiratansoares.github.io https://br.linkedin.com/in/ubiratanfsoares