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

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)

Ubiratan Soares

October 02, 2018
Tweet

More Decks by Ubiratan Soares

Other Decks in Programming

Transcript

  1. public class Human extends Animal { @Override public void walk()

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

    walk( ) - talk( ) - walk( ) - talk( ) INHERITANCE
  3. public class Car { final Engine engine; public Car(Engine e)

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

    { engine = e; } public void turnOn() { engine.start(); // TODO } }
  5. Engine Car - start( ) - turnOn( ) Animal Human

    - walk( ) - talk( ) - walk( ) - talk( ) Implements Extends Depends On
  6. public class NameValidator extends Validation { @Override public boolean validate(String

    input) { return input #$ null %& input.length() '( 50; } }
  7. 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
  8. Employee - calculatePayoff( ) - saveInfo( ) - describeEmployee( )

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

    And we if we add another method ? - findById( ) - reportWorkedHours( ) And we if we add another method ?
  10. HR Ops DBA Employee - calculatePayoff( ) - saveInfo( )

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

    function( ) function( ) function( ) HR Ops DBA Devs + Users Devs + Users Devs + Users
  12. • 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
  13. SINGLE RESPONSABILITY Each class / module should have one, and

    only one responsability inside our systems
  14. 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
  15. 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
  16. Engine.java Car.java - start( ) - turnOn( ) • A

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

    enforce that runtime dependencies mirror source-code dependencies
  19. Engine Car - start( flags) - turnOn( ) Engine.class Car.class

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

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

    O G* J H I D Recompilation cascade Re-deploying strategy ?
  22. 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); } }
  23. public class App { public static void main(String[] args) {

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

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

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

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

    oposite direction comparated to the related runtime dependency
  28. Engine.java Car.java - start( ) - turnOn( ) GasolineEngine.java -

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

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

    start( ) Car-v0.1.jar Engine-v0.2.jar BUILD SYSTEM Recompiled Re-deployed
  31. 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
  32. Payment Order - name( ) CreditcardPayment - name( ) -

    processor( ) - processor( ) BankslipPayment - name( ) - processor( ) CashPayment - name( ) - processor( )
  33. Payment Order - name( ) CreditcardPayment - fee( ) BankslipPayment

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

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

    implementations bloated with "un-needed" methods, and suffer the same issues from FAT classes
  36. How can we enable a new feature to some payment

    method - required by the Order - without leak this abstraction to other payment methods ???
  37. 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
  38. 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
  39. Integer Binary representation with C2 or C1 ?? LSB or

    MSB? 32-bit or 64-bit ? Signed or Unsigned
  40. 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 | | | | / | | | ------------------------------ ------------------------------
  41. 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(); }
  42. 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 ...
  43. 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'!
  44. Do I have a criteria in order to not make

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

    colaborator instance is from type A or B !!!!
  47. 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
  48. 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; } }
  49. 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; } }
  50. 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); } }
  51. 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); } }
  52. 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()); }
  53. 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()); }
  54. 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
  55. 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
  56. 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
  57. THE RIGHT WAY Square and Rectangle should be designed without

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

    relationships that they related concepts have in the real world" Representative Rule
  59. L.S.P. Remember the Representative Rule Avoid instanceOf as hell Subtypes

    must be perfectably substitutable Design your subtypes wisely
  60. 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
  61. 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
  62. 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)
  63. THE EXPECTATION The O.C.P. concept reflects the real vision of

    users and/or business for a software project
  64. 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
  65. 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
  66. UBIRATAN SOARES Computer Scientist by ICMC/USP Software Engineer, curious guy

    Google Developer Expert Teacher, speaker, etc, etc