The document discusses the S.O.L.I.D. principles of object-oriented design, which are Single responsibility principle, Open/closed principle, Liskov substitution principle, Interface segregation principle, and Dependency inversion principle. It provides examples of applying each principle to code, such as separating concerns into classes with single responsibilities, making classes extensible without modifying them, ensuring interface implementations are substitutable, splitting interfaces to avoid unnecessary implementations, and depending on abstractions rather than concretions. The document also discusses the differences between a developer and software engineer and demonstrates code applying some design principles.
2. • Senior Consultant at Deloitte Digital
• Worked on Mobile Applications and their respective backends.
• Experience ranges between Mobile Development, Software
engineering & scaling, and Large-Scale Hybrid Application
development
A LITTLE ABOUT ME
Jad Salhani
8. DO ONE THING WELL
The Single Responsibility principle can be
summarized by this sentence: “Do one thing, and
do it well.”
A responsibility is a “reason to change”, and a
class or a module should have only one reason to
change.
9. 1 <?php
2 namespace FBDevc;
3 use DB;
4
5 class OrdersReport
6 {
7 public function getOrdersInfo($startDate, $endDate)
8 {
9 $orders = $this->queryDBForOrders($startDate, $endDate);
10
11 return $this->format($orders);
12 }
13
14 protected function queryDBForOrders($startDate, $endDate)
15 { // If we would update our persistence layer in the future,
16 // we would have to do changes here too. <=> reason to change!
17 return DB::table('orders')->whereBetween('created_at', [$startDate, $endDate])->get();
18 }
19
20 protected function format($orders)
21 { // If we changed the way we want to format the output,
22 // we would have to make changes here. <=> reason to change!
23 return '<h1>Orders: ' . $orders . '</h1>';
24 }
25 }
10. 1 <?php
2 namespace Report;
3 use ReportRepositoriesOrdersRepository;
4
5 class OrdersReport
6 {
7 protected $repo;
8 protected $formatter;
9
10 public function __construct(OrdersRepository $repo, OrdersOutPutInterface $formatter)
11 {
12 $this->repo = $repo;
13 $this->formatter = $formatter;
14 }
15
16 public function getOrdersInfo($startDate, $endDate)
17 {
18 $orders = $this->repo->getOrdersWithDate($startDate, $endDate);
19
20 return $this->formatter->output($orders);
21 }
22 }
23
24 namespace Report;
25
26 interface OrdersOutPutInterface
27 {
28 public function output($orders);
29 }
30
31 namespace Report;
32
33 class HtmlOutput implements OrdersOutPutInterface
34 {
35 public function output($orders)
36 {
37 return '<h1>Orders: ' . $orders . '</h1>';
38 }
39
40 }
41
42 namespace ReportRepositories;
43 use DB;
44
45 class OrdersRepository
46 {
47 public function getOrdersWithDate($startDate, $endDate)
48 {
49 return DB::table('orders')->whereBetween('created_at', [$startDate, $endDate])->get();
50 }
51 }
12. Wikipedia
The Open / Closed Principle states that software entities
(classes, modules, functions, etc.) should be open for
extension, but closed for modification.
13. HUH?
• Software entities should be extendable without changing the
contents of the class you’re extending
• If Jad wants to use Salah and George, Salah and George
should have the same background so that Jad doesn’t
differentiate between them when executing his tasks.
14. 1 <?php
2 class Salah
3 {
4 public $language;
5 public $motivation;
6
7 public function __construct($language, $motivation)
8 {
9 $this->language = $language;
10 $this->motivation = $motivation;
11 }
12
13 public function mentor(){
14 $knowledge = "If you have enough $this->motivation, you can learn $this->language";
15 return $knowledge;
16 }
17 }
18
19 class George
20 {
21 public $language;
22
23 public function __construct($language)
24 {
25 $this->language = $language;
26 }
27
28 public function mentor(){
29 $knowledge = "To learn $this->language all you need is to believe in yourself";
30 return $knowledge;
31 }
32 }
33 class Jad
34 {
35 public function improveCommunity($mentorPerson)
36 {
37
38 if ($mentorPerson instanceof Salah) {
39 $knowledge = "Salah Knowledge: {$mentorPerson->mentor()}";
40 } else {
41 $knowledge = "George Knowledge: {$mentorPerson->mentor()}";
42 }
43
44 return $knowledge;
45 }
46 }
47
48 $salah = new Salah("Javascript", "A LOT");
49 $george = new George("Node");
50 $batal = new Jad();
51 echo $batal->improveCommunity($salah);
15. 1 <?php
2 interface FacebookLeadInterface
3 {
4 public function mentor();
5 }
6
7 class Salah implements FacebookLeadInterface
8 {
9 public $language;
10 public $motivation;
11
12 public function __construct($language, $motivation)
13 {
14 $this->language = $language;
15 $this->motivation = $motivation;
16 }
17
18 public function mentor(){
19 $knowledge = "If you have enough $this->motivation, you can learn $this->language";
20 return $knowledge;
21 }
22 }
23
24 class George implements FacebookLeadInterface
25 {
26 public $language;
27
28 public function __construct($language)
29 {
30 $this->language = $language;
31 }
32
33 public function mentor(){
34 $knowledge = "To learn $this->language all you need is to believe in yourself";
35 return $knowledge;
36 }
37 }
38
39 class Jad
40 {
41 public function improveCommunity(FacebookLeadInterface $person)
42 {
43 return "Facebook Lead Knowledge: {$person->mentor()}";
44 }
45 }
46
47 $salah = new Salah("Javascript", "A LOT");
48 $george = new George("Node");
49 $batal = new Jad();
50 echo $batal->improveCommunity($salah);
18. Any implementation of an abstraction
(interface) should be substitutable in any place
that the abstraction is accepted.
Two children who learned driving from their dad should be interchangeable as
drivers.
19. 1 <?php
2 interface FacebookLeadInterface
3 {
4 public function mentor(): string;
5 }
6
7 class Salah implements FacebookLeadInterface
8 {
9 public $language;
10 public $motivation;
11
12 public function __construct($language, $motivation)
13 {
14 $this->language = $language;
15 $this->motivation = $motivation;
16 }
17
18 public function mentor(){
19 $knowledge = "If you have enough $this->motivation, you can learn $this->language";
20 return $knowledge;
21 }
22 }
23
24 class George implements FacebookLeadInterface
25 {
26 public $language;
27
28 public function __construct($language)
29 {
30 $this->language = $language;
31 }
32
33 public function mentor(){
34
35 $knowledge = [
36 "To learn $this->language all you need is to believe in yourself",
37 "Sleep is overrated and for the weak",
38 "Working 18 hours a day is not wrong",
39 "Never stop working and improving"
40 ];
41 /*
42 Violates Liskov Substitution Principle because:
43 - the return type is different
44 - the consumer of this subclass and Salah won't work identically
45 */
46 // return $knowledge;
47
48 // to fix this
49 return join(", ", $knowledge);
50 }
51 }
22. Jad Salhani - 2020
You don’t use an Airbus 330 to take a flying tour of a city.
23. • A module should not be forced to implement parts of an
interface that it doesn’t use
• Interfaces and Contracts should be split as to have no
unnecessarily implemented methods.
24. SPOT THE DIFFERENCE
1 <?php
2 interface Workable
3 {
4 public function work();
5 public function sleep();
6 }
7
8 class HumanWorker implements Workable
9 {
10 public function work()
11 {
12 var_dump('works');
13 }
14
15 public function sleep()
16 {
17 var_dump('sleep');
18 }
19 }
20
21 class RobotWorker implements Workable
22 {
23 public function work()
24 {
25 var_dump('works');
26 }
27
28 public function sleep()
29 {
30 // No need
31 }
32 }
1 <?php
2 interface Workable
3 {
4 public function work();
5 }
6
7 interface Sleepable
8 {
9 public function sleep();
10 }
11
12 class HumanWorker implements Workable, Sleepable
13 {
14 public function work()
15 {
16 var_dump('works');
17 }
18
19 public function sleep()
20 {
21 var_dump('sleep');
22 }
23 }
24
25 class RobotWorker implements Workable
26 {
27 public function work()
28 {
29 var_dump('works');
30 }
31 }
27. • Not to be confused with Dependency
Injection
• High level modules should not depend on low
level modules
• All modules should depend on abstractions
not concretions
28. 1 <?php
2 class MySQLConnection
3 {
4 /**
5 * db connection
6 */
7 public function connect()
8 {
9 var_dump('MYSQL Connection');
10 }
11 }
12
13 class PasswordReminder
14 {
15 /**
16 * @var MySQLConnection
17 */
18 private $dbConnection;
19
20
21 public function __construct(MySQLConnection
$dbConnection)
22 {
23 $this->dbConnection = $dbConnection;
24 }
25 }
1 <?php
2 interface ConnectionInterface
3 {
4 public function connect();
5 }
6
7 class DbConnection implements ConnectionInterface
8 {
9
10 /**
11 * db connection
12 */
13 public function connect()
14 {
15 var_dump('MYSQL Connection');
16 }
17 }
18
19 class PasswordReminder
20 {
21 /**
22 * @var MySQLConnection
23 */
24
25 private $dbConnection;
26
27 public function __construct(ConnectionInterface
$dbConnection)
28 {
29 $this->dbConnection = $dbConnection;
30 }
31 }
SEPARATE YOUR LAYERS