I try to explain what SOLID principles are all about, how to refactor an example code according to these principles and summarize what are the benefits of writing a SOLID code.
2. WRITING SOLID CODE IN PRACTICE
ABOUT ME
Tomasz Wojcik (@prgTW)
• backend dev@backoffice team
• been with DocPlanner since dinosaurs
• worships Grumpy Cat, morning coffee
and code reviews in the middle of the night
ó actually :P
/prgTW /prgTW /prgTW
4. WRITING SOLID CODE IN PRACTICE
• SRP
• OCP
• LSP
• ISP
• DIP
WHAT “SOLID” STANDS FOR | THEORY
5. WRITING SOLID CODE IN PRACTICE
• SRP
• OCP
• LSP
• ISP
• DIP
Single Responsibility Principle
WHAT “SOLID” STANDS FOR | THEORY
6. WRITING SOLID CODE IN PRACTICE
• SRP
• OCP
• LSP
• ISP
• DIP
Single Responsibility Principle
Open/Closed Principle
WHAT “SOLID” STANDS FOR | THEORY
7. WRITING SOLID CODE IN PRACTICE
• SRP
• OCP
• LSP
• ISP
• DIP
Single Responsibility Principle
Open/Closed Principle
Liskov Substitution Principle
WHAT “SOLID” STANDS FOR | THEORY
8. WRITING SOLID CODE IN PRACTICE
• SRP
• OCP
• LSP
• ISP
• DIP
Single Responsibility Principle
Open/Closed Principle
Liskov Substitution Principle
Interface Segregation Principle
WHAT “SOLID” STANDS FOR | THEORY
9. WRITING SOLID CODE IN PRACTICE
• SRP
• OCP
• LSP
• ISP
• DIP
Single Responsibility Principle
Open/Closed Principle
Liskov Substitution Principle
Interface Segregation Principle
Dependency Inversion Principle
WHAT “SOLID” STANDS FOR | THEORY
20. WRITING SOLID CODE IN PRACTICE
• class/module should have only single responsibility
SINGLE RESPONSIBILITY PRINCIPLE | THEORY
21. WRITING SOLID CODE IN PRACTICE
• class/module should have only single responsibility
• the above is defined as single reason for change
SINGLE RESPONSIBILITY PRINCIPLE | THEORY
22. WRITING SOLID CODE IN PRACTICE
• class/module should have only single responsibility
• the above is defined as single reason for change
• related services should be aligned with this responsibility
SINGLE RESPONSIBILITY PRINCIPLE | THEORY
23. WRITING SOLID CODE IN PRACTICE
<?php
class RandomGithubEmoji
{
public function fetch(): string
{
$cacheFile = __DIR__ . '/random_emoji';
if (is_file($cacheFile) && time() - filemtime($cacheFile) <= 3)
{
return file_get_contents($cacheFile);
}
$response = file_get_contents('https://api.github.com/emojis');
if (!$response)
{
return 'https://assets-cdn.github.com/images/icons/emoji/trollface.png?v6';
}
$emojis = json_decode($response, true);
$randomEmoji = array_rand($emojis);
file_put_contents($cacheFile, $randomEmoji);
return $randomEmoji;
}
}
SINGLE RESPONSIBILITY PRINCIPLE | PRACTICE
24. WRITING SOLID CODE IN PRACTICE
<?php
class RandomGithubEmoji
{
public function fetch(): string
{
$cacheFile = __DIR__ . '/random_emoji';
if (is_file($cacheFile) && time() - filemtime($cacheFile) <= 3)
{
return file_get_contents($cacheFile);
}
$response = file_get_contents('https://api.github.com/emojis');
if (!$response)
{
return 'https://assets-cdn.github.com/images/icons/emoji/trollface.png?v6';
}
$emojis = json_decode($response, true);
$randomEmoji = array_rand($emojis);
file_put_contents($cacheFile, $randomEmoji);
return $randomEmoji;
}
}
$cacheFile = __DIR__ . '/random_emoji';
if (is_file($cacheFile) && time() - filemtime($cacheFile) <= 3)
{
return file_get_contents($cacheFile);
}
file_put_contents($cacheFile, $randomEmoji);
cache control
SINGLE RESPONSIBILITY PRINCIPLE | PRACTICE
25. WRITING SOLID CODE IN PRACTICE
<?php
class RandomGithubEmoji
{
public function fetch(): string
{
$cacheFile = __DIR__ . '/random_emoji';
if (is_file($cacheFile) && time() - filemtime($cacheFile) <= 3)
{
return file_get_contents($cacheFile);
}
$response = file_get_contents('https://api.github.com/emojis');
if (!$response)
{
return 'https://assets-cdn.github.com/images/icons/emoji/trollface.png?v6';
}
$emojis = json_decode($response, true);
$randomEmoji = array_rand($emojis);
file_put_contents($cacheFile, $randomEmoji);
return $randomEmoji;
}
}
$response = file_get_contents('https://api.github.com/emojis'); fetching data
SINGLE RESPONSIBILITY PRINCIPLE | PRACTICE
26. WRITING SOLID CODE IN PRACTICE
<?php
class RandomGithubEmoji
{
public function fetch(): string
{
$cacheFile = __DIR__ . '/random_emoji';
if (is_file($cacheFile) && time() - filemtime($cacheFile) <= 3)
{
return file_get_contents($cacheFile);
}
$response = file_get_contents('https://api.github.com/emojis');
if (!$response)
{
return 'https://assets-cdn.github.com/images/icons/emoji/trollface.png?v6';
}
$emojis = json_decode($response, true);
$randomEmoji = array_rand($emojis);
file_put_contents($cacheFile, $randomEmoji);
return $randomEmoji;
}
}
if (!$response)
{
return 'https://assets-cdn.github.com/images/icons/emoji/trollface.png?v6';
}
error handling
SINGLE RESPONSIBILITY PRINCIPLE | PRACTICE
27. WRITING SOLID CODE IN PRACTICE
<?php
class RandomGithubEmoji
{
public function fetch(): string
{
$cacheFile = __DIR__ . '/random_emoji';
if (is_file($cacheFile) && time() - filemtime($cacheFile) <= 3)
{
return file_get_contents($cacheFile);
}
$response = file_get_contents('https://api.github.com/emojis');
if (!$response)
{
return 'https://assets-cdn.github.com/images/icons/emoji/trollface.png?v6';
}
$emojis = json_decode($response, true);
$randomEmoji = array_rand($emojis);
file_put_contents($cacheFile, $randomEmoji);
return $randomEmoji;
}
}
$emojis = json_decode($response, true); data transformation
SINGLE RESPONSIBILITY PRINCIPLE | PRACTICE
28. WRITING SOLID CODE IN PRACTICE
<?php
class RandomGithubEmoji
{
public function fetch(): string
{
$cacheFile = __DIR__ . '/random_emoji';
if (is_file($cacheFile) && time() - filemtime($cacheFile) <= 3)
{
return file_get_contents($cacheFile);
}
$response = file_get_contents('https://api.github.com/emojis');
if (!$response)
{
return 'https://assets-cdn.github.com/images/icons/emoji/trollface.png?v6';
}
$emojis = json_decode($response, true);
$randomEmoji = array_rand($emojis);
file_put_contents($cacheFile, $randomEmoji);
return $randomEmoji;
}
}
$randomEmoji = array_rand($emojis);
return $randomEmoji;
random emoji logic
SINGLE RESPONSIBILITY PRINCIPLE | PRACTICE
29. WRITING SOLID CODE IN PRACTICE
• cache control
• fetching data
• error handling
• data transformation
• random emoji logic
SINGLE RESPONSIBILITY PRINCIPLE | PRACTICE
30. WRITING SOLID CODE IN PRACTICE
class FileCache• cache control
• fetching data
• error handling
• data transformation
• random emoji logic
SINGLE RESPONSIBILITY PRINCIPLE | PRACTICE
31. WRITING SOLID CODE IN PRACTICE
class FileCache
class FileGetContentsFetcher
• cache control
• fetching data
• error handling
• data transformation
• random emoji logic
SINGLE RESPONSIBILITY PRINCIPLE | PRACTICE
32. WRITING SOLID CODE IN PRACTICE
class FileCache
class FileGetContentsFetcher
throw new Exception
• cache control
• fetching data
• error handling
• data transformation
• random emoji logic
SINGLE RESPONSIBILITY PRINCIPLE | PRACTICE
custom one
33. WRITING SOLID CODE IN PRACTICE
class FileCache
class FileGetContentsFetcher
throw new Exception
class JsonSerializer
• cache control
• fetching data
• error handling
• data transformation
• random emoji logic
SINGLE RESPONSIBILITY PRINCIPLE | PRACTICE
custom one
34. WRITING SOLID CODE IN PRACTICE
class FileCache
class FileGetContentsFetcher
throw new Exception
class JsonSerializer
class RandomGithubEmoji
• cache control
• fetching data
• error handling
• data transformation
• random emoji logic
SINGLE RESPONSIBILITY PRINCIPLE | PRACTICE
custom one
35. WRITING SOLID CODE IN PRACTICE
<?php
class RandomGithubEmoji
{
public function fetch(): string
{
$response = file_get_contents('https://api.github.com/emojis');
if (!$response)
{
return 'https://assets-cdn.github.com/images/icons/emoji/trollface.png?v6';
}
$emojis = json_decode($response, true);
$randomEmoji = array_rand($emojis);
return $randomEmoji;
}
}
$cacheFile = __DIR__ . '/random_emoji';
if (is_file($cacheFile) && time() - filemtime($cacheFile) <= 3)
{
return file_get_contents($cacheFile);
}
file_put_contents($cacheFile, $randomEmoji);
cache control
SINGLE RESPONSIBILITY PRINCIPLE | PRACTICE
36. WRITING SOLID CODE IN PRACTICE
<?php
class RandomGithubEmoji
{
public function fetch(): string
{
$response = file_get_contents('https://api.github.com/emojis');
if (!$response)
{
return 'https://assets-cdn.github.com/images/icons/emoji/trollface.png?v6';
}
$emojis = json_decode($response, true);
$randomEmoji = array_rand($emojis);
return $randomEmoji;
}
}
SINGLE RESPONSIBILITY PRINCIPLE | PRACTICE
cache control
$cache = new FileCache(__DIR__ . '/random_emoji');
$randomEmoji = $cache->fetch();
if (null !== $randomEmoji)
{
return $randomEmoji;
}
$cache->put($randomEmoji);
37. WRITING SOLID CODE IN PRACTICE
<?php
class RandomGithubEmoji
{
public function fetch(): string
{
$cacheFile = __DIR__ . '/random_emoji';
if (is_file($cacheFile) && time() - filemtime($cacheFile) <= 3)
{
return file_get_contents($cacheFile);
}
if (!$response)
{
return 'https://assets-cdn.github.com/images/icons/emoji/trollface.png?v6';
}
$emojis = json_decode($response, true);
$randomEmoji = array_rand($emojis);
file_put_contents($cacheFile, $randomEmoji);
return $randomEmoji;
}
}
$response = file_get_contents('https://api.github.com/emojis'); fetching data
SINGLE RESPONSIBILITY PRINCIPLE | PRACTICE
38. WRITING SOLID CODE IN PRACTICE
<?php
class RandomGithubEmoji
{
public function fetch(): string
{
$cacheFile = __DIR__ . '/random_emoji';
if (is_file($cacheFile) && time() - filemtime($cacheFile) <= 3)
{
return file_get_contents($cacheFile);
}
if (!$response)
{
return 'https://assets-cdn.github.com/images/icons/emoji/trollface.png?v6';
}
$emojis = json_decode($response, true);
$randomEmoji = array_rand($emojis);
file_put_contents($cacheFile, $randomEmoji);
return $randomEmoji;
}
}
SINGLE RESPONSIBILITY PRINCIPLE | PRACTICE
fetching data
$response = (new FileGetContentsFetcher)->fetch('https://api.github.com/emojis');
39. WRITING SOLID CODE IN PRACTICE
return 'https://assets-cdn.github.com/images/icons/emoji/trollface.png?v6'; error handling
SINGLE RESPONSIBILITY PRINCIPLE | PRACTICE
<?php
class RandomGithubEmoji
{
public function fetch(): string
{
$cacheFile = __DIR__ . '/random_emoji';
if (is_file($cacheFile) && time() - filemtime($cacheFile) <= 3)
{
return file_get_contents($cacheFile);
}
$response = file_get_contents('https://api.github.com/emojis');
if (!$response)
{
}
$emojis = json_decode($response, true);
$randomEmoji = array_rand($emojis);
file_put_contents($cacheFile, $randomEmoji);
return $randomEmoji;
}
}
40. WRITING SOLID CODE IN PRACTICE
SINGLE RESPONSIBILITY PRINCIPLE | PRACTICE
error handling
throw new UnableToFetchEmojiException;
<?php
class RandomGithubEmoji
{
public function fetch(): string
{
$cacheFile = __DIR__ . '/random_emoji';
if (is_file($cacheFile) && time() - filemtime($cacheFile) <= 3)
{
return file_get_contents($cacheFile);
}
$response = file_get_contents('https://api.github.com/emojis');
if (!$response)
{
}
$emojis = json_decode($response, true);
$randomEmoji = array_rand($emojis);
file_put_contents($cacheFile, $randomEmoji);
return $randomEmoji;
}
}
41. WRITING SOLID CODE IN PRACTICE
<?php
class RandomGithubEmoji
{
public function fetch(): string
{
$cacheFile = __DIR__ . '/random_emoji';
if (is_file($cacheFile) && time() - filemtime($cacheFile) <= 3)
{
return file_get_contents($cacheFile);
}
$response = file_get_contents('https://api.github.com/emojis');
if (!$response)
{
return 'https://assets-cdn.github.com/images/icons/emoji/trollface.png?v6';
}
$randomEmoji = array_rand($emojis);
file_put_contents($cacheFile, $randomEmoji);
return $randomEmoji;
}
}
$emojis = json_decode($response, true); data transformation
SINGLE RESPONSIBILITY PRINCIPLE | PRACTICE
42. WRITING SOLID CODE IN PRACTICE
<?php
class RandomGithubEmoji
{
public function fetch(): string
{
$cacheFile = __DIR__ . '/random_emoji';
if (is_file($cacheFile) && time() - filemtime($cacheFile) <= 3)
{
return file_get_contents($cacheFile);
}
$response = file_get_contents('https://api.github.com/emojis');
if (!$response)
{
return 'https://assets-cdn.github.com/images/icons/emoji/trollface.png?v6';
}
$randomEmoji = array_rand($emojis);
file_put_contents($cacheFile, $randomEmoji);
return $randomEmoji;
}
}
SINGLE RESPONSIBILITY PRINCIPLE | PRACTICE
data transformation
$emojis = (new JsonSerializer)->deserialize($response, 'json');
43. WRITING SOLID CODE IN PRACTICE
<?php
class RandomGithubEmoji
{
public function fetch(): string
{
$cache = new FileCache(__DIR__ . '/random_emoji');
$randomEmoji = $cache->fetch();
if (null !== $randomEmoji)
{
return $randomEmoji;
}
$response = (new FileGetContentsFetcher)->fetch('https://api.github.com/emojis');
if (!$response)
{
throw new UnableToFetchEmojiException;
}
$emojis = (new JsonSerializer)->deserialize($response, 'json');
$randomEmoji = array_rand($emojis);
$cache->put($randomEmoji);
return $randomEmoji;
}
}
SINGLE RESPONSIBILITY PRINCIPLE | PRACTICE
44. WRITING SOLID CODE IN PRACTICE
<?php
class RandomGithubEmoji
{
public function fetch(): string
{
$cache = new FileCache(__DIR__ . '/random_emoji');
$randomEmoji = $cache->fetch();
if (null !== $randomEmoji)
{
return $randomEmoji;
}
$response = (new FileGetContentsFetcher)->fetch('https://api.github.com/emojis');
if (!$response)
{
throw new UnableToFetchEmojiException;
}
$emojis = (new JsonSerializer)->deserialize($response, 'json');
$randomEmoji = array_rand($emojis);
$cache->put($randomEmoji);
return $randomEmoji;
}
}
new FileCache(__DIR__ . '/random_emoji');
new FileGetContentsFetcher
new JsonSerializer
DEPENDENCY INJECTION | PRACTICE
47. WRITING SOLID CODE IN PRACTICE
• used to decouple software modules
DEPENDENCY INVERSION PRINCIPLE | THEORY
48. WRITING SOLID CODE IN PRACTICE
• used to decouple software modules
• high-level modules shouldn’t depend on low-level-modules, they both should
depend on abstractions
DEPENDENCY INVERSION PRINCIPLE | THEORY
49. WRITING SOLID CODE IN PRACTICE
• used to decouple software modules
• high-level modules shouldn’t depend on low-level-modules, they both should
depend on abstractions
• class members should be interfaces or abstracts
DEPENDENCY INVERSION PRINCIPLE | THEORY
50. WRITING SOLID CODE IN PRACTICE
• used to decouple software modules
• high-level modules shouldn’t depend on low-level-modules, they both should
depend on abstractions
• class members should be interfaces or abstracts
• class shouldn’t derive from concretions (composition over inheritance)
DEPENDENCY INVERSION PRINCIPLE | THEORY
51. WRITING SOLID CODE IN PRACTICE
• used to decouple software modules
• high-level modules shouldn’t depend on low-level-modules, they both should
depend on abstractions
• class members should be interfaces or abstracts
• class shouldn’t derive from concretions (composition over inheritance)
• methods shouldn’t derive from already implemented methods
DEPENDENCY INVERSION PRINCIPLE | THEORY
52. WRITING SOLID CODE IN PRACTICE
• used to decouple software modules
• high-level modules shouldn’t depend on low-level-modules, they both should
depend on abstractions
• class members should be interfaces or abstracts
• class shouldn’t derive from concretions (composition over inheritance)
• methods shouldn’t derive from already implemented methods
• variables must be created via injection or creational patterns (eg. factories)
DEPENDENCY INVERSION PRINCIPLE | THEORY
53. WRITING SOLID CODE IN PRACTICE
DEPENDENCY INVERSION PRINCIPLE | THEORY
https://en.wikipedia.org/wiki/Dependency_inversion_principle
54. WRITING SOLID CODE IN PRACTICE
DEPENDENCY INVERSION PRINCIPLE | PRACTICE
55. WRITING SOLID CODE IN PRACTICE
DEPENDENCY INVERSION PRINCIPLE | PRACTICE
56. WRITING SOLID CODE IN PRACTICE
DEPENDENCY INVERSION PRINCIPLE | THEORY
https://en.wikipedia.org/wiki/Dependency_inversion_principle
57. WRITING SOLID CODE IN PRACTICE
DEPENDENCY INVERSION PRINCIPLE | THEORY
https://en.wikipedia.org/wiki/Dependency_inversion_principle
58. WRITING SOLID CODE IN PRACTICE
DEPENDENCY INVERSION PRINCIPLE | PRACTICE
59. WRITING SOLID CODE IN PRACTICE
DEPENDENCY INVERSION PRINCIPLE | PRACTICE
60. WRITING SOLID CODE IN PRACTICE
DEPENDENCY INJECTION
(NOT PART OF SOLID ACTUALLY)
62. WRITING SOLID CODE IN PRACTICE
• providing dependencies from outside of the dependent class
DEPENDENCY INJECTION | THEORY
63. WRITING SOLID CODE IN PRACTICE
• providing dependencies from outside of the dependent class
• class mustn’t use new or static methods
DEPENDENCY INJECTION | THEORY
64. WRITING SOLID CODE IN PRACTICE
<?php
class RandomGithubEmoji
{
/** @var Cache Interface */
private $cache ;
/** @var FetcherInterface */
private $fetcher;
/** @var SerializerInterface */
private $serializer;
public function __construct(Cache Interface $cache , FetcherInterface $fetcher, SerializerInterface $serializer)
{
$this->cache = $cache ;
$this->fetcher = $fetcher;
$this->serializer = $serializer;
}
// ...
}
DEPENDENCY INJECTION | PRACTICE
68. WRITING SOLID CODE IN PRACTICE
• class should be open for extension
• class behaviour can be changed in derived classes
• protected instead of private methods
OPEN/CLOSED PRINCIPLE | THEORY
69. WRITING SOLID CODE IN PRACTICE
• class should be open for extension
• class behaviour can be changed in derived classes
• protected instead of private methods
• class should be closed for modification
• there should be no reason to change class code
• interfaces
OPEN/CLOSED PRINCIPLE | THEORY
70. WRITING SOLID CODE IN PRACTICE
• class should be open for extension
• class behaviour can be changed in derived classes
• protected instead of private methods
• class should be closed for modification
• there should be no reason to change class code
• interfaces
• think of dependencies coming from 3rd party vendors
OPEN/CLOSED PRINCIPLE | THEORY
71. WRITING SOLID CODE IN PRACTICE
OPEN/CLOSED PRINCIPLE | PRACTICE
<?php
interface RandomEmojiInterface
{
public function fetch(): string
}
72. WRITING SOLID CODE IN PRACTICE
OPEN/CLOSED PRINCIPLE | PRACTICE
<?php
interface RandomEmojiInterface
{
public function fetch(): string
}
73. WRITING SOLID CODE IN PRACTICE
<?php
class Emoji
{
/** @var string */
private $url;
public function __construct(string $url)
{
$this->url = $url;
}
public function getUrl(): string
{
return $this->url;
}
}
interface RandomEmojiInterface
{
public function fetch(): Emoji
}
OPEN/CLOSED PRINCIPLE | PRACTICE
74. WRITING SOLID CODE IN PRACTICE
<?php
class Emoji
{
/** @var string */
private $url;
public function __construct(string $url)
{
$this->url = $url;
}
public function getUrl(): string
{
return $this->url;
}
}
interface RandomEmojiInterface
{
public function fetch(): Emoji
}
OPEN/CLOSED PRINCIPLE | PRACTICE
75. WRITING SOLID CODE IN PRACTICE
/** @var string */
private $url;
public function __construct(string $url)
{
$this->url = $url;
}
public function getUrl(): string
{
return $this->url;
}
<?php
interface EmojiInterface
{
public function getUrl(): string;
// here we COULD add additional features
}
class Emoji implements EmojiInterface
{
}
interface RandomEmojiInterface
{
public function fetch(): EmojiInterface;
}
OPEN/CLOSED PRINCIPLE | PRACTICE
78. WRITING SOLID CODE IN PRACTICE
• many specific interfaces are better than one generic
INTERFACE SEGREGATION PRINCIPLE | THEORY
79. WRITING SOLID CODE IN PRACTICE
• many specific interfaces are better than one generic
• client shouldn’t be aware and forced to depend on methods it doesn’t use
INTERFACE SEGREGATION PRINCIPLE | THEORY
80. WRITING SOLID CODE IN PRACTICE
• many specific interfaces are better than one generic
• client shouldn’t be aware and forced to depend on methods it doesn’t use
• helps keeping parts of the system decoupled
INTERFACE SEGREGATION PRINCIPLE | THEORY
81. WRITING SOLID CODE IN PRACTICE
INTERFACE SEGREGATION PRINCIPLE | PRACTICE
82. WRITING SOLID CODE IN PRACTICE
<?php
interface CacheInterface
{
public function get($key);
public function put($something);
}
class FileCache implements CacheInterface
{
// get, put
}
INTERFACE SEGREGATION PRINCIPLE | PRACTICE
83. WRITING SOLID CODE IN PRACTICE
<?php
interface CacheInterface
{
public function get($key);
public function put($something);
public function getMany(array $keys);
public function putMany(array $items);
}
class FileCache implements CacheInterface
{
// get, put
// getMany, putMany
}
INTERFACE SEGREGATION PRINCIPLE | PRACTICE
84. WRITING SOLID CODE IN PRACTICE
<?php
interface CacheInterface
{
public function get($key);
public function put($something);
public function getMany(array $keys);
public function putMany(array $items);
public function invalidate($key);
public function invalidateMany(array $keys);
public function invalidataAll();
}
class FileCache implements CacheInterface
{
// get, put
// getMany, putMany
// invalidate, invalidateMany, invalidateAll
}
INTERFACE SEGREGATION PRINCIPLE | PRACTICE
85. WRITING SOLID CODE IN PRACTICE
<?php
interface CacheInterface
{
public function get($key);
public function put($something);
public function getMany(array $keys);
public function putMany(array $items);
public function invalidate($key);
public function invalidateMany(array $keys);
public function invalidataAll();
public function gc();
public function countAll();
}
class FileCache implements CacheInterface
{
// :(
}
INTERFACE SEGREGATION PRINCIPLE | PRACTICE
86. WRITING SOLID CODE IN PRACTICE
INTERFACE SEGREGATION PRINCIPLE | PRACTICE
87. WRITING SOLID CODE IN PRACTICE
INTERFACE SEGREGATION PRINCIPLE | PRACTICE
88. WRITING SOLID CODE IN PRACTICE
INTERFACE SEGREGATION PRINCIPLE | PRACTICE
89. WRITING SOLID CODE IN PRACTICE
LISKOV SUBSTITUTION PRINCIPLE
(BARBARA LISKOV)
90. WRITING SOLID CODE IN PRACTICE
https://en.wikipedia.org/wiki/Covariance_and_contravariance_(computer_science)
COVARIANCE & CONTRAVARIANCE | LSP | THEORY
91. WRITING SOLID CODE IN PRACTICE
https://en.wikipedia.org/wiki/Covariance_and_contravariance_(computer_science)
COVARIANCE & CONTRAVARIANCE | LSP | THEORY
92. WRITING SOLID CODE IN PRACTICE
https://en.wikipedia.org/wiki/Covariance_and_contravariance_(computer_science)
COVARIANCE & CONTRAVARIANCE | LSP | THEORY
93. WRITING SOLID CODE IN PRACTICE
https://en.wikipedia.org/wiki/Covariance_and_contravariance_(computer_science)
COVARIANCE & CONTRAVARIANCE | LSP | THEORY
94. WRITING SOLID CODE IN PRACTICE
https://en.wikipedia.org/wiki/Covariance_and_contravariance_(computer_science)
COVARIANCE & CONTRAVARIANCE | LSP | THEORY
95. WRITING SOLID CODE IN PRACTICE
https://en.wikipedia.org/wiki/Covariance_and_contravariance_(computer_science)
COVARIANCE & CONTRAVARIANCE | LSP | THEORY
97. WRITING SOLID CODE IN PRACTICE
• objects of type T can be replaced by any subtype S of type T without altering
desired properties of the program/module (eg. correctness)
LISKOV SUBSTITUTION PRINCIPLE | THEORY
98. WRITING SOLID CODE IN PRACTICE
• objects of type T can be replaced by any subtype S of type T without altering
desired properties of the program/module (eg. correctness)
• signature requirements
• covariance of return types
• contravariance of method arguments
• no new exceptions being non-derived from exceptions thrown in super type
LISKOV SUBSTITUTION PRINCIPLE | THEORY
99. WRITING SOLID CODE IN PRACTICE
• objects of type T can be replaced by any subtype S of type T without altering
desired properties of the program/module (eg. correctness)
• signature requirements
• covariance of return types
• contravariance of method arguments
• no new exceptions being non-derived from exceptions thrown in super type
• behavioral conditions
• preconditions cannot be strengthen in subtype
• postconditions cannot be weakened in subtype
• invariant in supertypes have to be preserved in subtype
LISKOV SUBSTITUTION PRINCIPLE | THEORY
100. WRITING SOLID CODE IN PRACTICE
COVARIANCE & CONTRAVARIANCE | LSP | PRACTICE
101. WRITING SOLID CODE IN PRACTICE
COVARIANCE & CONTRAVARIANCE | LSP | PRACTICE
<?php
interface T
{
}
interface S extends T
{
}
102. WRITING SOLID CODE IN PRACTICE
<?php
interface T
{
}
interface S extends T
{
}
// ---------------------------
interface A
{
public function do(T $t);
}
interface B extends A
{
public function do(T $t);
}
COVARIANCE & CONTRAVARIANCE | LSP | PRACTICE
<?php
interface T
{
}
interface S extends T
{
}
103. WRITING SOLID CODE IN PRACTICE
<?php
interface T
{
}
interface S extends T
{
}
// ---------------------------
interface A
{
public function do(T $t);
}
interface B extends A
{
public function do(T $t);
}
COVARIANCE & CONTRAVARIANCE | LSP | PRACTICE
<?php
interface T
{
}
interface S extends T
{
}
invariance of method argument
104. WRITING SOLID CODE IN PRACTICE
COVARIANCE & CONTRAVARIANCE | LSP | PRACTICE
105. WRITING SOLID CODE IN PRACTICE
<?php
interface T
{
}
interface S extends T
{
}
// ---------------------------
interface A
{
public function do(T $t);
}
interface B extends A
{
public function do(S $s);
}
COVARIANCE & CONTRAVARIANCE | LSP | PRACTICE
106. WRITING SOLID CODE IN PRACTICE
<?php
interface T
{
}
interface S extends T
{
}
// ---------------------------
interface A
{
public function do(T $t);
}
interface B extends A
{
public function do(S $s);
}
COVARIANCE & CONTRAVARIANCE | LSP | PRACTICE
covariance of method argument
107. WRITING SOLID CODE IN PRACTICE
<?php
interface T
{
}
interface S extends T
{
}
// ---------------------------
interface A
{
public function do(T $t);
}
interface B extends A
{
public function do(S $s);
}
COVARIANCE & CONTRAVARIANCE | LSP | PRACTICE
covariance of method argument
not allowed here, why?
108. WRITING SOLID CODE IN PRACTICE
<?php
interface T
{
}
interface S extends T
{
}
// ---------------------------
interface A
{
public function do(T $t);
}
interface B extends A
{
public function do(S $s);
}
COVARIANCE & CONTRAVARIANCE | LSP | PRACTICE
covariance of method argument
not allowed here, why?
… violating A::do signature
… violating “preconditions cannot be strengthen in subtype”
109. WRITING SOLID CODE IN PRACTICE
COVARIANCE & CONTRAVARIANCE | LSP | PRACTICE
110. WRITING SOLID CODE IN PRACTICE
<?php
interface T
{
}
interface S extends T
{
}
// ---------------------------
class A
{
public function do(): S;
{
}
}
class B extends A
{
public function do(): T;
{
}
}
COVARIANCE & CONTRAVARIANCE | LSP | PRACTICE
111. WRITING SOLID CODE IN PRACTICE
<?php
interface T
{
}
interface S extends T
{
}
// ---------------------------
class A
{
public function do(): S;
{
}
}
class B extends A
{
public function do(): T;
{
}
}
COVARIANCE & CONTRAVARIANCE | LSP | PRACTICE
contravariance of return type
112. WRITING SOLID CODE IN PRACTICE
<?php
interface T
{
}
interface S extends T
{
}
// ---------------------------
class A
{
public function do(): S;
{
}
}
class B extends A
{
public function do(): T;
{
}
}
COVARIANCE & CONTRAVARIANCE | LSP | PRACTICE
contravariance of return type
not allowed here, why?
113. WRITING SOLID CODE IN PRACTICE
<?php
interface T
{
}
interface S extends T
{
}
// ---------------------------
class A
{
public function do(): S;
{
}
}
class B extends A
{
public function do(): T;
{
}
}
COVARIANCE & CONTRAVARIANCE | LSP | PRACTICE
contravariance of return type
not allowed here, why?
… violating A::do signature
… violating “postconditions cannot be weakened in subtype”
119. WRITING SOLID CODE IN PRACTICE
BENEFITS | SUMMARY
• code is well organised and more declarative
• code is easy to read and understand (short learning curve)
• code is loosely coupled and thus reusable
• code is easy to adapt
• code is easy to extend
• code is easy to refactor
• code is easy to unit test
• code is easy to replace/mock in functional tests
• … etc. etc.