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
PRO

October 02, 2018
Tweet

More Decks by Ubiratan Soares

Other Decks in Programming

Transcript

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

    View Slide

  2. View Slide

  3. ROBERT
    MARTIN
    "Uncle Bob"

    View Slide

  4. View Slide

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

    View Slide

  6. OBJECT

    ORIENTATION

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  16. ABSTRACTIONS

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  20. public interface CheckSession {
    boolean isActive();
    }

    View Slide

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

    View Slide

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

    View Slide

  23. S.O.L.I.D.

    View Slide

  24. Single
    Responsability
    Principle

    View Slide

  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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  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

    View Slide

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

    View Slide

  34. SRP
    VIOLATION
    An Activity talk directly with
    database

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  38. SRP
    VIOLATION
    Singletons instances are
    retrieved inside local method

    View Slide

  39. SRP
    VIOLATION
    Static Logging

    View Slide

  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

    View Slide

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

    View Slide

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

    View Slide

  43. Dependency
    Inversion
    Principle

    View Slide

  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

    View Slide

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

    View Slide

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

    View Slide

  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

    View Slide

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

    View Slide

  49. But …

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  54. Can this be different?

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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


    View Slide

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

    View Slide

  63. BEHOLD THE
    DEPENDENCY
    INVERSION

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  69. Interfaces
    Segregation
    Principle

    View Slide

  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

    View Slide

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

    View Slide

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

    View Slide

  73. Several
    features
    later …

    View Slide

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

    View Slide

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

    View Slide

  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

    View Slide

  77. LEAKY
    ABSTRACTIONS
    IN PRACTICE

    View Slide

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

    View Slide

  79. Rely on the
    most relevant
    interface
    feature !!!

    View Slide

  80. A
    B C

    View Slide

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

    View Slide

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

    View Slide

  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

    View Slide

  84. Lyskov
    Substitution
    Principle

    View Slide

  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

    View Slide

  86. TYPES

    View Slide

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

    View Slide

  88. What can I do with
    an Integer, anyway ?

    View Slide

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

    View Slide

  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 |
    | | | / | | |
    ------------------------------ ------------------------------

    View Slide

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

    View Slide

  92. SUBTYPE
    Class or interface that
    extends some type

    View Slide

  93. TYPE SAFETY

    View Slide

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

    View Slide

  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 ...

    View Slide

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

    View Slide

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

    View Slide

  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."

    View Slide

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

    View Slide

  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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  115. Open
    Closed
    Principle

    View Slide

  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

    View Slide

  117. View Slide

  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

    View Slide

  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)

    View Slide

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

    View Slide

  121. Users
    Business
    Developers
    Product

    View Slide

  122. How can we deal
    with software
    entropy ?

    View Slide

  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

    View Slide

  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

    View Slide

  125. Feelings
    Report !!!

    View Slide

  126. View Slide

  127. View Slide

  128. https://speakerdeck.com/ubiratansoares

    View Slide

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

    View Slide

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

    View Slide