Design Principles - A SOLID Introduction

Design Principles - A SOLID Introduction

This presentation gives an overview of the SOLID principles. What they are and what they mean. With examples in PHP.

9227671c02701253b64e469823671635?s=128

Tom Van Herreweghe

September 04, 2018
Tweet

Transcript

  1. DESIGN PRINCIPLES DESIGN PRINCIPLES A SOLID INTRODUCTION A SOLID INTRODUCTION

  2. WHAT ARE THE SOLID WHAT ARE THE SOLID PRINCIPLES? PRINCIPLES?

  3. Set of 5 Design Principles Robert C. Martin - "Uncle

    Bob" Agile methodology Design Principles and Design Patterns (2000)
  4. CODE BECOMES CODE BECOMES more understandable more flexible & decoupled

    easier to adapt & maintain
  5. QUIZ TIME! QUIZ TIME! Single Responsibility Principle Open/Closed Principle Liskov

    Substitution Principle Interface Segregation Principle Dependency Inversion Principle
  6. WHAT DO THEY MEAN? WHAT DO THEY MEAN? Let's find

    out together! ¯\_( ツ)_/¯
  7. SINGLE RESPONSIBILITY SINGLE RESPONSIBILITY PRINCIPLE PRINCIPLE

  8. A class should have only 1 responsibility

  9. None
  10. A class should have only 1 responsibility reason to change

  11. None
  12. class PostalCodeHelper { /* ... */ public function getCity(string $zip)

    : string { return $this->map[$zip] ?? 'Black Hole'; } public function format(string $zip) : string { $city = $this->mapCodeToCity($zip); return sprintf( '%1$s %2$s', $zip, $city
  13. REASONS FOR CHANGING: REASONS FOR CHANGING: Mapping changes Formatting changes

    Importing changes ...
  14. WHO WANTS TO MAINTAIN THIS? WHO WANTS TO MAINTAIN THIS?

  15. HOW TO FIX? HOW TO FIX? Extract Postal Code Mapper

    Extract Postal Code Repository Extract Postal Code Formatter Extract Postal Code Importer ...
  16. BUT ISN'T THIS MORE WORK? BUT ISN'T THIS MORE WORK?

    absolutely!
  17. SO WHAT IS THE BENEFIT? SO WHAT IS THE BENEFIT?

    Clear & concise classes, which are easier to grasp and therefore easier to maintain.
  18. HEURISTICS & CONVENTIONS HEURISTICS & CONVENTIONS Think before coding Keep

    classes small Extract whenever you detect a new responsibility Don't be lazy
  19. ANY QUESTIONS? ANY QUESTIONS?

  20. OPEN/CLOSED PRINCIPLE OPEN/CLOSED PRINCIPLE

  21. A CLASS/MODULE/PACKAGE SHOULD BE A CLASS/MODULE/PACKAGE SHOULD BE Open for

    extension Closed for modification
  22. 2 INTERPRETATIONS 2 INTERPRETATIONS Meyer's Open/Closed Principle Polymorphic Open/Closed Principle

  23. MEYER'S OPEN/CLOSED PRINCIPLE MEYER'S OPEN/CLOSED PRINCIPLE Implementation Inheritance Introduces tight

    coupling
  24. POLYMORPHIC OPEN/CLOSED PRINCIPLE POLYMORPHIC OPEN/CLOSED PRINCIPLE Relies on abstractions Abstracts

    are closed for modification Implementation can change
  25. class Circle { /* ... */ } class Rectangle {

    /* ... */ }
  26. $shapes = [ new Circle, new Circle, new Rectangle, new

    Circle, ];
  27. class ShapeDrawer { public function draw(array $shapes) : void {

    foreach ($shapes as $shape) { if ($shape instanceof Circle) { // ... } if ($shape instanceof Rectangle) { // ... } } } }
  28. VIOLATES OPEN/CLOSED PRINCIPLE VIOLATES OPEN/CLOSED PRINCIPLE New shape? => Modify

    ShapeDrawer => ShapeDrawer is not closed
  29. HOW TO FIX? HOW TO FIX? Define an abstraction Depend

    on abstraction (see later on) Implement different behaviors (polymorphism)
  30. interface Shape { public function draw(): void; } class Circle

    implements Shape { /* ... */ } class Rectangle implements Shape { /* ... */ }
  31. class ShapeDrawer { public function draw(array $shapes) : void {

    foreach ($shapes as $shape) { if (! $shape instanceof Shape) { continue; } $shape->draw(); } } }
  32. HEURISTICS & CONVENTIONS HEURISTICS & CONVENTIONS Work towards closed classes

    Make functionality private Mark classes & methods as final No RTTI Specify abstractions
  33. ANY QUESTIONS? ANY QUESTIONS?

  34. LISKOV SUBSTITUTION LISKOV SUBSTITUTION PRINCIPLE PRINCIPLE

  35. LISKOV WHO? LISKOV WHO?

  36. BARBARA LISKOV BARBARA LISKOV

  37. BARBARA LISKOV BARBARA LISKOV American Computer Scientist MIT Turing Award

    winner Fields: Operating systems Programming languages Distributed systems Abstractions & modularity
  38. LSP LSP When B is a subtype of A, Then

    objects of type A may be replaced By objects of type B, Without altering the desirable properties Of the program
  39. LSP LSP When B is a child of parent A,

    Then objects of parent A may be replaced By objects of child B, Without altering the desirable properties Of the program
  40. CLASSIC CLASSIC WRONG WRONG EXAMPLE EXAMPLE

  41. class Rectangle { // ... public function setWidth(int $width) :

    void { $this->width = $width; } // ... }
  42. None
  43. class Square extends Rectangle { // ... public function setWidth(int

    $width) : void { $this->width = $width; $this->height = $height; } // ... }
  44. WHO CAN EXPLAIN THE WHO CAN EXPLAIN THE UNDESIRABLE UNDESIRABLE

    CHANGE? CHANGE?
  45. function specifyDimensions(Rectangle $rectangle) { $rectangle->setWidth(100); $rectangle->setHeight(200); } $square = new

    Square; specifyDimensions($square); assert($square->getWidth() === 100); // fail? assert($square->getHeight() === 200); // fail?
  46. UNSURE ABOUT THE CORRECTNESS UNSURE ABOUT THE CORRECTNESS

  47. INFLUENCE OF LSP INFLUENCE OF LSP Standard requirements on method

    signature in OOP languages
  48. INFLUENCE OF LSP INFLUENCE OF LSP Contravariance of method arguments

    in subtype Covariance of return types in subtype
  49. None
  50. CAN ANYONE EXPLAIN VARIANCE? CAN ANYONE EXPLAIN VARIANCE?

  51. None
  52. Contravariance = Parameter type widening (less specific) Covariance = Return

    type narrowing (more specific)
  53. SOME EXAMPLES IN PHP SOME EXAMPLES IN PHP

  54. QUIZ! - WILL THIS WORK? (1/4) QUIZ! - WILL THIS

    WORK? (1/4) class Foo { public function bar() {} } class Woo extends Foo { public function bar() : int {} }
  55. QUIZ! - WILL THIS WORK? (2/4) QUIZ! - WILL THIS

    WORK? (2/4) class Foo { public function bar() : Rectangle {} } class Woo extends Foo { public function bar() : Square {} }
  56. PHP IS "1-LEVEL" COVARIANT PHP IS "1-LEVEL" COVARIANT Only works

    from "mixed" to "something else" Not from Supertype to Subtype
  57. QUIZ! - WILL THIS WORK? (3/4) QUIZ! - WILL THIS

    WORK? (3/4) class Foo { public function bar(string $baz) : int {} } class Woo extends Foo { public function bar($baz) : int {} }
  58. QUIZ! - WILL THIS WORK? (4/4) QUIZ! - WILL THIS

    WORK? (4/4) class Foo { public function bar(Square $baz) : int {} } class Woo extends Foo { public function bar(Rectangle $baz) : int {} }
  59. PHP IS "1-LEVEL" CONTRAVARIANT PHP IS "1-LEVEL" CONTRAVARIANT Only works

    from "something" to "mixed" (omitting) Not from Subtype to Supertype (scope widening) (only in PHP >= 7.2)
  60. CONCLUSION: PHP IS INVARIANT CONCLUSION: PHP IS INVARIANT

  61. HEURISTICS & CONVENTIONS HEURISTICS & CONVENTIONS LSP is about behavior.

    Think carefully when replaceing with a subtype PHP is Invariant Typehints can be omitted in subtypes Mixed return types can be specified in subtypes
  62. ANY QUESTIONS? ANY QUESTIONS?

  63. TIME FOR A <BR /> TIME FOR A <BR />

  64. INTERFACE SEGREGATION INTERFACE SEGREGATION PRINCIPLE PRINCIPLE

  65. https://speakerdeck.com/miljar/there-is-an-i-in-solid

  66. No object should be forced to depend on methods it

    doesn't use
  67. Split large interfaces into smaller, more specific role interfaces

  68. Voorbeeldje

  69. HEURISTICS & CONVENTIONS HEURISTICS & CONVENTIONS When you need to

    implement more than you'd like, split the interface Implementation class Foo implements A, B, C interface Bar extends A, C
  70. INTERESTING READ INTERESTING READ Matthias Noback: When to add an

    interface to a class
  71. ANY QUESTIONS? ANY QUESTIONS?

  72. DEPENDENCY INVERSION DEPENDENCY INVERSION PRINCIPLE PRINCIPLE

  73. Abstractions should not depend on details. Details should depend on

    abstractions. High-level modules should not depend on low-level modules. Both should depend on abstractions.
  74. final class InMemoryRegistry { }

  75. interface ContextBuilder { public function setLocator(InMemoryRegistry $locator) : vo }

  76. ABSTRACTIONS SHOULD NOT DEPEND ABSTRACTIONS SHOULD NOT DEPEND ON DETAILS

    ON DETAILS
  77. interface ContextBuilderLocator { public function retrieve(string $key) : ContextBuilder; }

    final class InMemoryRegistry implements ContextBuilderLocator { }
  78. interface ContextBuilder { public function setLocator(ContextBuilderLocator $locator) }

  79. HIGH-LEVEL MODULES SHOULD NOT HIGH-LEVEL MODULES SHOULD NOT DEPEND ON

    LOW-LEVEL MODULES DEPEND ON LOW-LEVEL MODULES
  80. High Level: domain logic; further away from infrastructure Low Level:

    framework logic; closer to infrastructure
  81. https://speakerdeck.com/miljar/hexagonal- architecture-or-how-to-completely-overengineer-your- application

  82. HEURISTICS & CONVENTIONS HEURISTICS & CONVENTIONS

  83. CREATE ABSTRACTIONS ACCORDING THE CREATE ABSTRACTIONS ACCORDING THE OPEN/CLOSED PRINCIPLE

    OPEN/CLOSED PRINCIPLE interface ContactRepository
  84. DEPEND ON THESE ABSTRACTIONS DEPEND ON THESE ABSTRACTIONS public function

    __construct(ContactRepository $repo)
  85. MAKE LOW LEVEL SPECIFIC IMPLEMENTATIONS & MAKE LOW LEVEL SPECIFIC

    IMPLEMENTATIONS & NAME ACCORDINGLY NAME ACCORDINGLY DbalContactRepository or InMemoryContactRepository
  86. IT CAN HELP TO "PHYSICALLY" SPLIT FILES BETWEEN IT CAN

    HELP TO "PHYSICALLY" SPLIT FILES BETWEEN HIGH & LOW LEVEL HIGH & LOW LEVEL
  87. ANY QUESTIONS? ANY QUESTIONS?

  88. WRAPPIN' IT UP WRAPPIN' IT UP Single Responsibility Principle Open/Closed

    Principle Liskov Substitution Principle Interface Segregation Principle Dependency Inversion Principle
  89. HTTPS://JOIND.IN/TALK/B543A HTTPS://JOIND.IN/TALK/B543A

  90. THANKS! THANKS!