Make Your Project SOLID
PHP Conference Argentina 2013
Benjamin Eberlei, @beberlei
4th October 2013
About me

Helping people to create high quality web applications.

http://qafoo.com
Doctrine Developer
Symfony Contributor...
About me

Helping people to create high quality web applications.

http://qafoo.com
Doctrine Developer
Symfony Contributor...
About me

Helping people to create high quality web applications.

http://qafoo.com
Doctrine Developer
Symfony Contributor...
About me

Helping people to create high quality web applications.

http://qafoo.com
Doctrine Developer
Symfony Contributor...
About me

Helping people to create high quality web applications.

http://qafoo.com
Doctrine Developer
Symfony Contributor...
Why Object Orientation?

So, why do you want object oriented code?
Manage complexity
Reusable code
Maintainable code
Why Object Orientation?

So, why do you want object oriented code?
Manage complexity
Reusable code
Maintainable code
Why Object Orientation?

So, why do you want object oriented code?
Manage complexity
Reusable code
Maintainable code
Why Object Orientation?

So, why do you want object oriented code?
Manage complexity
Reusable code
Maintainable code
The SOLID principles

5 essential principles of object oriented design
Introduced by Robert C. Martin (Uncle Bob)
not the ...
By Example
A weather loader component
Fetch weather for a city
Relevant data:
Condition
Temperature
Wind

Be service-agnos...
Single Responsibility Principle

“There should never be more than one
reason for a class to change.”
The Issue
1

<?php

2
3

class GoogleWeatherService

4

{

5

public function getWeatherForLocation ( L o c a t i o n $ l ...
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 , G...
Single Responsibility Principle

One responsibility per class
Separation of concerns
Responsibilities hard to detect
Single Responsibility Principle

One responsibility per class
Separation of concerns
Responsibilities hard to detect
Open/Close Principle

“Software entities (classes, modules,
functions, etc.) should be open for
extension, but closed for ...
The Wrong Way
3

class WeatherLoader

4

{
public function
{ /∗ . . . ∗/ }

5
6

construct ( $service )

7
8

public funct...
The Right Way

3

class WeatherLoader

4

{
public function
{ /∗ . . . ∗/ }

5
6

c o n s t r u c t ( WeatherService $ s e...
Open/Close Principle

Changes introduce errors
Especially cascading changes

Ideally: Write once, change never!
Extend sof...
Liskov Substitution Principle

“Functions that use pointers or references
to base classes must be able to use objects
of d...
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 f...
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
...
Liskov Substitution Principle

Be less strict on input
Be more strict on output
Liskov Substitution Principle
Be less strict on input
Be more strict on output

float

milesToKilometers($miles)

float
Liskov Substitution Principle
Be less strict on input
Be more strict on output

float
>0

milesToKilometers($miles)

float
Liskov Substitution Principle
Be less strict on input
Be more strict on output

float

milesToKilometers($miles)
string or
...
Liskov Substitution Principle
Be less strict on input
Be more strict on output

float
or string

milesToKilometers($miles)
...
Liskov Substitution Principle
Be less strict on input
Be more strict on output

float

milesToKilometers($miles)
>0
float
Liskov Substitution Principle

Do not change contracts by inheritance
Methods must work as expected in derived classes
Use...
Liskov Substitution Principle

Do not change contracts by inheritance
Methods must work as expected in derived classes
Use...
Dependency Inversion Principle

“A. High-level modules should not depend
on low level modules. Both should depend
on abstr...
Dependency Inversion Principle

“A. High-level modules should not depend
on low level modules. Both should depend
on abstr...
The Issue

1

<?php

2
3

class WeatherLoader

4

{

6

public function
construct (
GoogleWeatherService $weatherService ,...
Doing it Right

1

<?php

2
3

class WeatherLoader

4

{

6

public function
construct (
WeatherService $weatherService , ...
Dependency Inversion Principle

Use abstraction to encapsulate low level modules
Abstractions are the APIs
Abstractions hi...
Dependency Inversion Principle

Use abstraction to encapsulate low level modules
Abstractions are the APIs
Abstractions hi...
Dependency Inversion Principle

Use abstraction to encapsulate low level modules
Abstractions are the APIs
Abstractions hi...
Dependency Inversion Principle

Use abstraction to encapsulate low level modules
Abstractions are the APIs
Abstractions hi...
Interface Segregation Principle

“Clients should not be forced to depend
upon interfaces that they do not use.”
Suboptimal

3

class Loader

4

{
public function
{ /∗ . . . ∗/ }

5
6

c o n s t r u c t ( WeatherService $weatherService...
The Fix
1

interface LocationWeatherProvider

2

{
function getWeatherForLocation ( L o c a t i o n $ l o c a t i o n ) ;
...
The Fix
1

interface LocationWeatherProvider

2

{
function getWeatherForLocation ( L o c a t i o n $ l o c a t i o n ) ;
...
Interface Segregation Principle

Avoid not needed dependencies
Design interfaces from a usage point of view
Do not let unn...
The SOLID principles

Single Responsibility Principle
Open/Closed Principle
Liskov Substitution Principle
Interface Segreg...
2013 - Benjamin Eberlei: Hacé tu proyecto SOLID!
Upcoming SlideShare
Loading in...5
×

2013 - Benjamin Eberlei: Hacé tu proyecto SOLID!

140

Published on

PHP Conference Argentina

Published in: Technology
0 Comments
0 Likes
Statistics
Notes
  • Be the first to comment

  • Be the first to like this

No Downloads
Views
Total Views
140
On Slideshare
0
From Embeds
0
Number of Embeds
0
Actions
Shares
0
Downloads
1
Comments
0
Likes
0
Embeds 0
No embeds

No notes for slide

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
  1. A particular slide catching your eye?

    Clipping is a handy way to collect important slides you want to go back to later.

×