2013 - Benjamin Eberlei: Hacé tu proyecto SOLID!

326 views

Published on

PHP Conference Argentina

Published in: Technology
  • Be the first to comment

  • Be the first to like this

2013 - Benjamin Eberlei: Hacé tu proyecto SOLID!

  1. 1. Make Your Project SOLID PHP Conference Argentina 2013 Benjamin Eberlei, @beberlei 4th October 2013
  2. 2. About me Helping people to create high quality web applications. http://qafoo.com Doctrine Developer Symfony Contributor Twitter @beberlei and @qafoo
  3. 3. About me Helping people to create high quality web applications. http://qafoo.com Doctrine Developer Symfony Contributor Twitter @beberlei and @qafoo
  4. 4. About me Helping people to create high quality web applications. http://qafoo.com Doctrine Developer Symfony Contributor Twitter @beberlei and @qafoo
  5. 5. About me Helping people to create high quality web applications. http://qafoo.com Doctrine Developer Symfony Contributor Twitter @beberlei and @qafoo
  6. 6. About me Helping people to create high quality web applications. http://qafoo.com Doctrine Developer Symfony Contributor Twitter @beberlei and @qafoo
  7. 7. Why Object Orientation? So, why do you want object oriented code? Manage complexity Reusable code Maintainable code
  8. 8. Why Object Orientation? So, why do you want object oriented code? Manage complexity Reusable code Maintainable code
  9. 9. Why Object Orientation? So, why do you want object oriented code? Manage complexity Reusable code Maintainable code
  10. 10. Why Object Orientation? So, why do you want object oriented code? Manage complexity Reusable code Maintainable code
  11. 11. The SOLID principles 5 essential principles of object oriented design Introduced by Robert C. Martin (Uncle Bob) not the inventor of the principles Have proven to lead to better code Scientific background (partly)
  12. 12. By Example A weather loader component Fetch weather for a city Relevant data: Condition Temperature Wind Be service-agnostic Weather service come and go Data licenses may change Log service failures Make it possible to add service fallbacks later
  13. 13. Single Responsibility Principle “There should never be more than one reason for a class to change.”
  14. 14. The Issue 1 <?php 2 3 class GoogleWeatherService 4 { 5 public function getWeatherForLocation ( L o c a t i o n $ l o c a t i o n ) 6 { $xml = $ t h i s −> getData ( $ l o c a t i o n ) ; $weather = $ t h i s −> e x t r a c t W e a t h e r ( $xml ) ; r e t u r n $weather ; 7 8 9 } 10 11 12 protected function e x t r a c t W e a t h e r ( $xml ) 13 { $weather = new Weather ( ) ; $weather −> c o n d i t i o n s = $ t h i s −> p a r s e C o n d i t i o n s ( $xml ) ; // ... $weather −>windSpeed = $ t h i s −> c o n v e r t M i l e s T o K i l o m e t e r ( $ t h i s −>parseWindSpeed ( $xml ) ); r e t u r n $weather ; 14 15 16 17 18 19 20 } 21 22 /∗ . . . ∗/ 23 24 }
  15. 15. The Fix 1 <?php 2 3 class GoogleWeatherService 4 { public function construct ( H t t p C l i e n t $ c l i e n t , GoogleDataParser $ p a r s e r ) { /∗ . . . ∗/ } 5 6 7 8 public function getWeatherForLocation ( L o c a t i o n $ l o c a t i o n ) 9 { 10 $xml = $ t h i s −> c l i e n t −> g e t ( s p r i n t f ( ’ h t t p : / / . . . / ? c i t y=%s ’ , $ l o c a t i o n −> c i t y ) ); r e t u r n $ t h i s −> parser −> parseWeather ( $xml ) ; 11 12 13 14 15 } 16 17 }
  16. 16. Single Responsibility Principle One responsibility per class Separation of concerns Responsibilities hard to detect
  17. 17. Single Responsibility Principle One responsibility per class Separation of concerns Responsibilities hard to detect
  18. 18. Open/Close Principle “Software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification.”
  19. 19. The Wrong Way 3 class WeatherLoader 4 { public function { /∗ . . . ∗/ } 5 6 construct ( $service ) 7 8 public function getWeatherForLocation ( S t r u c t L o c a t i o n $ l o c a t i o n ) 9 { 11 // ... switch ( g e t c l a s s ( $ t h i s −> s e r v i c e ) ) 12 { 10 case ’ GoogleWeatherService ’ : r e t u r n $ t h i s −> s e r v i c e −> getWeather ( $ l o c a t i o n ) ; 13 14 15 case ’ WetterComWeatherService ’ : r e t u r n $ t h i s −> s e r v i c e −> r e t r i e v e W e a t h e r ( $ l o c a t i o n −> c i t y , $ l o c a t i o n −> c o u n t r y ); // ... 16 17 18 19 20 } 21 } 22 23 }
  20. 20. The Right Way 3 class WeatherLoader 4 { public function { /∗ . . . ∗/ } 5 6 c o n s t r u c t ( WeatherService $ s e r v i c e ) 7 8 public function getWeatherForLocation ( S t r u c t L o c a t i o n $ l o c a t i o n ) 9 { // ... r e t u r n $ t h i s −> s e r v i c e −> getWeatherForLocation ( $ l o c a t i o n ) ; 10 11 } 12 13 }
  21. 21. Open/Close Principle Changes introduce errors Especially cascading changes Ideally: Write once, change never! Extend software only by new code New interface implementations Inheritance Aggregation
  22. 22. Liskov Substitution Principle “Functions that use pointers or references to base classes must be able to use objects of derived classes without knowing it.”
  23. 23. A Simple Class 1 <?php 2 3 class D i s t a n c e C o n v e r t e r 4 { const FACTOR = 0 . 6 2 1 4 ; 5 6 7 public function m i l e s T o K i l o m e t e r s ( $ m i l e s ) 8 { r e t u r n $ m i l e s / s e l f : : FACTOR; 9 } 10 11 }
  24. 24. Getting into Trouble 1 <?php 2 3 class F o r m a t t i n g D i s t a n c e C o n v e r t e r extends DistanceConverer 4 { 5 public function m i l e s T o K i l o m e t e r s ( $ m i l e s ) 6 { 7 i f ( $miles < 0 ) 8 { throw new I n v a l i d A r g u m e n t E x c e p t i o n ( ) ; 9 10 } 11 return s p r i n t f ( ’ %01.2 f km ’ , p a r e n t : : m i l e s T o K i l o m e t e r s ( $ m i l e s ) ); 12 13 } 14 15 }
  25. 25. Liskov Substitution Principle Be less strict on input Be more strict on output
  26. 26. Liskov Substitution Principle Be less strict on input Be more strict on output float milesToKilometers($miles) float
  27. 27. Liskov Substitution Principle Be less strict on input Be more strict on output float >0 milesToKilometers($miles) float
  28. 28. Liskov Substitution Principle Be less strict on input Be more strict on output float milesToKilometers($miles) string or float
  29. 29. Liskov Substitution Principle Be less strict on input Be more strict on output float or string milesToKilometers($miles) float
  30. 30. Liskov Substitution Principle Be less strict on input Be more strict on output float milesToKilometers($miles) >0 float
  31. 31. Liskov Substitution Principle Do not change contracts by inheritance Methods must work as expected in derived classes Users must not distinguish between super- and subclass Subtype polymorphism
  32. 32. Liskov Substitution Principle Do not change contracts by inheritance Methods must work as expected in derived classes Users must not distinguish between super- and subclass Subtype polymorphism
  33. 33. Dependency Inversion Principle “A. High-level modules should not depend on low level modules. Both should depend on abstractions.” “B. Abstractions should not depend upon details. Details should depend upon abstractions.”
  34. 34. Dependency Inversion Principle “A. High-level modules should not depend on low level modules. Both should depend on abstractions.” “B. Abstractions should not depend upon details. Details should depend upon abstractions.”
  35. 35. The Issue 1 <?php 2 3 class WeatherLoader 4 { 6 public function construct ( GoogleWeatherService $weatherService , F i l e L o g g e r $ l o g g e r ) 7 { 5 $ t h i s −> w ea th e rS er v ic e = $weatherService ; $ t h i s −> l o g g e r = $logger ; 8 9 10 } 11 public function getWeatherForLocation ( L o c a t i o n $ l o c a t i o n ) 12 { // ... $ t h i s −> l o g g e r −> l o g ( ’Some l o g message . ’ ) ; // ... $ t h i s −> l o g g e r −> w r i t e T o F i l e ( ) ; 13 14 15 16 } 17 18 }
  36. 36. Doing it Right 1 <?php 2 3 class WeatherLoader 4 { 6 public function construct ( WeatherService $weatherService , Logger $ l o g g e r ) 7 { 5 $ t h i s −> w ea th e rS er v ic e = $weatherService ; $ t h i s −> l o g g e r = $logger ; 8 9 10 } 11 public function getWeatherForLocation ( L o c a t i o n $ l o c a t i o n ) 12 { // ... $ t h i s −> l o g g e r −> l o g ( ’Some l o g message . ’ ) ; // ... 13 14 15 } 16 17 }
  37. 37. Dependency Inversion Principle Use abstraction to encapsulate low level modules Abstractions are the APIs Abstractions hide implementation details Depend on interfaces, not realizations Define interfaces from a usage point of view Finding abstractions is not easy Dependency Injection, anyone?
  38. 38. Dependency Inversion Principle Use abstraction to encapsulate low level modules Abstractions are the APIs Abstractions hide implementation details Depend on interfaces, not realizations Define interfaces from a usage point of view Finding abstractions is not easy Dependency Injection, anyone?
  39. 39. Dependency Inversion Principle Use abstraction to encapsulate low level modules Abstractions are the APIs Abstractions hide implementation details Depend on interfaces, not realizations Define interfaces from a usage point of view Finding abstractions is not easy Dependency Injection, anyone?
  40. 40. Dependency Inversion Principle Use abstraction to encapsulate low level modules Abstractions are the APIs Abstractions hide implementation details Depend on interfaces, not realizations Define interfaces from a usage point of view Finding abstractions is not easy Dependency Injection, anyone?
  41. 41. Interface Segregation Principle “Clients should not be forced to depend upon interfaces that they do not use.”
  42. 42. Suboptimal 3 class Loader 4 { public function { /∗ . . . ∗/ } 5 6 c o n s t r u c t ( WeatherService $weatherService , Logger $ l o g g e r ) 7 public function getWeatherForLocation ( L o c a t i o n $ l o c a t i o n ) { /∗ . . . ∗/ } 8 9 10 } 3 a b s t r a c t class WeatherService 4 { a b s t r a c t public function getWeatherForLocation ( L o c a t i o n $ l o c a t i o n ) ; 5 6 a b s t r a c t public function g e t F o r e c a s t F o r L o c a t i o n ( L o c a t i o n $ l o c a t i o n , $weekDay ) ; 7 8 }
  43. 43. The Fix 1 interface LocationWeatherProvider 2 { function getWeatherForLocation ( L o c a t i o n $ l o c a t i o n ) ; 3 4 } 1 a b s t r a c t class WeatherService implements L o c a t i o n W e a t h e r P r o v i d e r 2 { a b s t r a c t public function getWeatherForLocation ( L o c a t i o n $ l o c a t i o n ) ; 3 4 a b s t r a c t public function g e t F o r e c a s t F o r L o c a t i o n ( L o c a t i o n $ l o c a t i o n , $weekDay ) ; 5 6 } 1 class Loader 2 { public function { /∗ . . . ∗/ } 3 4 c o n s t r u c t ( L o c a t i o n W e a t h e r P r o v i d e r $ p r o v i d e r , Logger $ l o g g e r ) 5 public function getWeatherForLocation ( L o c a t i o n $ l o c a t i o n ) { /∗ . . . ∗/ } 6 7 8 }
  44. 44. The Fix 1 interface LocationWeatherProvider 2 { function getWeatherForLocation ( L o c a t i o n $ l o c a t i o n ) ; 3 4 } 1 a b s t r a c t class WeatherService implements L o c a t i o n W e a t h e r P r o v i d e r 2 { a b s t r a c t public function getWeatherForLocation ( L o c a t i o n $ l o c a t i o n ) ; 3 4 a b s t r a c t public function g e t F o r e c a s t F o r L o c a t i o n ( L o c a t i o n $ l o c a t i o n , $weekDay ) ; 5 6 } 1 class Loader 2 { public function { /∗ . . . ∗/ } 3 4 c o n s t r u c t ( L o c a t i o n W e a t h e r P r o v i d e r $ p r o v i d e r , Logger $ l o g g e r ) 5 public function getWeatherForLocation ( L o c a t i o n $ l o c a t i o n ) { /∗ . . . ∗/ } 6 7 8 }
  45. 45. Interface Segregation Principle Avoid not needed dependencies Design interfaces from a usage point of view Do not let unnecessary functionality float in
  46. 46. The SOLID principles Single Responsibility Principle Open/Closed Principle Liskov Substitution Principle Interface Segregation Principle Dependency Inversion Principle

×