Save 37% off PRO during our Black Friday Sale! »

Immutability to save an ever-changing world

Immutability to save an ever-changing world

Want to build software that is more testable, easier to modify, and has fewer lines of code? Architecting with more immutable objects that are always in a valid state is the most important lesson I have learned in building better software applications. Using immutable value objects will lead to less checking, fewer bugs, and more DRY code, and will help avoid the “spooky action at a distance” problem in PHP. We will also learn how to use immutable objects and immutable collections to improve design of our mutable entities. Lastly, we’ll see how immutable objects and functional programming can reduce complexity.

F4bb45b2a18ee44c4a28b1664de150bd?s=128

Andrew Cassell

October 16, 2018
Tweet

Transcript

  1. Andrew Cassell @alc277 andrewcassell.com Immutability to Save an Ever-Changing World

  2. None
  3. None
  4. None
  5. Immutability & Invariants & Encapsulation & Behavior

  6. Good Code?

  7. None
  8. None
  9. $car->bark(); $dog->drive(); It was not supposed to be about cars

    barking or dogs driving
  10. None
  11. BEER

  12. Immutability & Invariants & Encapsulation & Behavior

  13. Mutable Immutable Read-Only After Creation Read & Write

  14. None
  15. None
  16. None
  17. None
  18. None
  19. None
  20. None
  21. None
  22. None
  23. Passed in by Reference / Pointer

  24. None
  25. None
  26. None
  27. None
  28. Spooky Action at a Distance

  29. None
  30. None
  31. None
  32. None
  33. None
  34. None
  35. Mutable objects depend on time.

  36. Mutable = Value(t) Immutable = Value()

  37. $mutable->function(a,b,c) { global $time; //… }

  38. None
  39. What should be Immutable?

  40. Event

  41. Event History

  42. Request

  43. Response

  44. Command

  45. Almost Everything

  46. HOW?

  47. Immutability in PHP

  48. Plain Old PHP Objects (POPOs)

  49. • POPOs • No setters, all parameters are passed into

    constructor • Declare all class properties as private • No properties that are mutable objects Immutable Objects in PHP
  50. None
  51. None
  52. None
  53. None
  54. None
  55. Composite Immutable Objects

  56. None
  57. None
  58. None
  59. None
  60. None
  61. None
  62. None
  63. None
  64. None
  65. Reflection API

  66. More Classes & Files

  67. Performance & Memory Usage

  68. Immutability & Invariants & Encapsulation & Behavior

  69. Immutability & Invariants & Encapsulation & Behavior

  70. OBJECTS FOR ALL THE THINGS

  71. Not Just Static Typing https://github.com/Fiedzia/type-system-research/blob/master/README.md

  72. Value Objects

  73. None
  74. • POPOs • No setters, all parameters are passed into

    constructor • Declare all class properties as private • No properties that are mutable objects Immutable Objects in PHP
  75. • Immutable Objects • Always in a valid state •

    Validate in constructor • Behavioral methods return new object • Not dependencies Value Objects in PHP
  76. Always Valid Value Object Immutable No Identity (Only Values) Recipe

    Name Weight Temperature Entity Identifiable Mutable Lifecycle Contains Value Objects Line Item Brewer Recipe
  77. First Name Last Name Full Name Email Address Recipe Name

    Date Brewed On Recipe ID Temperature Liters Gallons Kilograms Pounds Value Objects
  78. Value Objects

  79. None
  80. “Gateway Drug to Test Driven Development” Value Objects

  81. “Gateway Drug to Test Driven Development” Value Objects #MYTESTSDONTPASS

  82. RecipeName Example

  83. None
  84. None
  85. cassell:beeriously cassell$ docker run --rm --interactive --tty --network beeriously_default --volume

    `pwd`:/app --user : --workdir /app beeriously_php-fpm /app/vendor/bin/phpunit --configuration /app/src/Tests/Unit/ phpunit.xml.dist PHPUnit 6.4.3 by Sebastian Bergmann and contributors. FEE 3 / 3 (100%) Time: 2.34 seconds, Memory: 4.00MB There were 2 errors: 1) Beeriously\Tests\Unit\Domain\Recipe\RecipeNameTest::testGetter Error: Class 'Beeriously\Domain\Recipe\RecipeName' not found /app/src/Tests/Unit/Domain/Recipe/RecipeNameTest.php:20 2) Beeriously\Tests\Unit\Domain\Recipe\RecipeNameTest::testToString Error: Class 'Beeriously\Domain\Recipe\RecipeName' not found /app/src/Tests/Unit/Domain/Recipe/RecipeNameTest.php:26 -- There was 1 failure: 1) Beeriously\Tests\Unit\Domain\Recipe\RecipeNameTest::testEmptyFails Failed asserting that exception of type "Error" matches expected exception "Beeriously\Domain\Recipe\InvalidRecipeNameException". Message was: "Class 'Beeriously\Domain\Recipe\RecipeName' not found" at /app/src/Tests/Unit/Domain/Recipe/RecipeNameTest.php:15 . ERRORS! Tests: 3, Assertions: 1, Errors: 2, Failures: 1.
  86. None
  87. None
  88. None
  89. cassell:beeriously cassell$ docker run --rm --interactive --tty --network beeriously_default --volume

    `pwd`:/app --user : --workdir /app beeriously_php-fpm /app/vendor/bin/phpunit --configuration /app/src/Tests/ Unit/phpunit.xml.dist PHPUnit 6.4.3 by Sebastian Bergmann and contributors. ... 3 / 3 (100%) Time: 192 ms, Memory: 4.00MB OK (3 tests, 4 assertions)
  90. None
  91. BrewedOn Example

  92. None
  93. Pounds Example

  94. None
  95. None
  96. None
  97. <?php var_dump(-0.0 > 0); // false var_dump(-0.0 < 0); //

    false var_dump(-0.0 === 0.0); // true (float) var_dump(-0.0 === 0); // false (int) var_dump(-0.0 == false); // true (bool) var_dump(-0.0 == null); // true (bool) var_dump(-0.0 == null); // true (string) var_dump(-0.0 == "negativezero"); // true (string)
  98. Immutability & Invariants & Encapsulation & Behavior

  99. Eliminate Checking Value Objects

  100. None
  101. None
  102. None
  103. None
  104. None
  105. Static Constructors

  106. Natural Language Constructors

  107. None
  108. None
  109. Immutability & Invariants & Encapsulation & Behavior

  110. None
  111. $poundsA->reduceBy($poundsB);

  112. if(!is_numeric($a)) { throw new \Exception(…) } if(!is_numeric($b)) { throw new

    \Exception(…) } if($a < $b) { throw new \Exception(…) } $pounds = $a - $b;
  113. Don’t Extend

  114. class Pounds extends FloatValue { //… }

  115. class EmailAddress extends StringObject { //… }

  116. None
  117. None
  118. Composite Value Objects

  119. Immutability & Invariants & Encapsulation & Behavior

  120. Full Name First Name + Last Name

  121. None
  122. Full Name Salutation + First Name Middle Name(s) + Last

    Name + Suffix
  123. Encoding Business Logic in Value Objects

  124. Alcohol by Volume

  125. None
  126. Sugar + Yeast = Alcohol + CO 2 C H

    O ->2C H OH + 2CO 6 12 6 2 5 2
  127. Sugar + Yeast = Alcohol + CO2

  128. Sugar - Sugar = Alcohol START FINISH

  129. Sugar - Sugar = Alcohol START FINISH Volume Volume

  130. Hydrometer

  131. learntomoonshine.com

  132. Michael L. Hall - Zymurgy, Summer 1995, vol. 18, no.

    2
  133. None
  134. Calibration Temperature Minimum Gravity Reading Maximum Gravity Reading

  135. None
  136. None
  137. None
  138. None
  139. None
  140. None
  141. None
  142. None
  143. None
  144. None
  145. None
  146. None
  147. None
  148. None
  149. None
  150. None
  151. None
  152. None
  153. None
  154. None
  155. None
  156. None
  157. None
  158. None
  159. Immutable Value Objects Typical RecipeName String Temperature Float(7,3) DateBrewed DateTime

    Gravity Float(7,3) ABV Function + Validation?
  160. Immutability & Invariants & Encapsulation & Behavior

  161. Entities

  162. Identifiable Have State and are Mutable (Lifecycle) Operate using Value

    Objects *(Ideally) Storage Agnostic *(Ideally) Never in an Invalid State Entities
  163. None
  164. None
  165. BEHAVIOR FIRST! STORAGE SECOND! “Storage Agnostic”

  166. SETTERS ARE BAD

  167. None
  168. None
  169. None
  170. None
  171. Calling two setters in a row on the same object

    Missing a Concept?
  172. Calling two methods in a row on the same object

    Missing a Concept?
  173. Passing more than one parameter to a method Missing a

    Concept?
  174. None
  175. None
  176. None
  177. DO AS MUCH AS YOU CAN IN VALUE OBJECTS!

  178. None
  179. Doctrine

  180. None
  181. None
  182. None
  183. None
  184. None
  185. None
  186. None
  187. None
  188. What is actually an entity?

  189. None
  190. None
  191. None
  192. None
  193. None
  194. None
  195. None
  196. Immutable “Entity”

  197. Immutable Book Mutable Price

  198. $book->setPrice(16.96); $price = new Price($book,15.00) $price->setAmount(16.96); Mutable Immutable

  199. $book->setPrice(16.96); $amount = new Amount(16.96, new Currency(‘USD’); $price = new

    BookPrice($book, $amount); $price->change(new Amount(15.99, new Currency(‘USD’)); Mutable Immutable
  200. Immutable Collections

  201. None
  202. None
  203. None
  204. Value Object

  205. None
  206. “Immutable” Collection of Entities

  207. None
  208. None
  209. None
  210. None
  211. None
  212. None
  213. None
  214. None
  215. None
  216. None
  217. None
  218. Functional Programming

  219. Procedural Programming

  220. $recipe = new Recipe(//…); $recipe->addThisIngredient(//…); $recipe->addWater(//…); $recipe->addThatIngredient(//…); $recipe->boil(//…); $recipe->getEstimatedABV(); //…

    === inputs
  221. $recipe = new Recipe($time + //…); $recipe->addThisIngredient($time + //…); $recipe->addWater($time

    + //…); $recipe->addThatIngredient($time + //…); $recipe->boil($time + //…); $recipe->getEstimatedABV($time);
  222. $recipe = new Recipe($history + //…); $recipe->addThisIngredient($history + //…); $recipe->addWater($history

    + //…); $recipe->addThatIngredient($history + //…); $recipe->boil($history + //…); $recipe->getEstimatedABV($history);
  223. Functional Programming

  224. None
  225. None
  226. Functional programming with mutable objects is more difficult than with

    immutable objects.
  227. Don’t Do It.

  228. Functional Programming?

  229. Procedural with Immutable Objects

  230. Objected Oriented Programming with Immutable Objects

  231. Final Thoughts

  232. None
  233. homebrewersassociation.org

  234. None
  235. PHP BEER BREWING SOFTWARE FOR BITTER DEVELOPERS WHO WANT TO

    BREW EVEN MORE BITTER BEERS And never heard of not-invented-here and wanna learn and do other good stuff too.
  236. None
  237. None
  238. None
  239. https://www.youtube.com/watch?v=O3T9-FilHVI

  240. None
  241. Think Immutable First

  242. https://joind.in/talk/aabc8 Thank You! @alc277