Skip to content

Commit 117fbdf

Browse files
committed
refs #119 init SOLID page
1 parent 1f64432 commit 117fbdf

File tree

8 files changed

+500
-1
lines changed

8 files changed

+500
-1
lines changed
+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
@extends('layout')
2+
@section('title', 'SOLID принципы')
3+
@section('description', 'Мы поочередно познакомимся с каждым принципом, чтобы понять, как SOLID может помочь вам стать лучшим разработчиком.')
4+
@section('content')
5+
6+
<x-header align="align-items-end">
7+
<x-slot name="sup">Объектно-ориентированные</x-slot>
8+
<x-slot name="title">SOLID принципы</x-slot>
9+
<x-slot name="description">
10+
Мы поочередно познакомимся с каждым принципом, чтобы понять, как SOLID может помочь вам стать лучшим разработчиком.
11+
</x-slot>
12+
<x-slot name="content">
13+
<figure class="d-none d-md-block">
14+
<x-icon path="l.dots" class="text-primary opacity-2 d-block mx-auto" height="300" width="300"/>
15+
</figure>
16+
</x-slot>
17+
</x-header>
18+
19+
@php
20+
$sections = collect([
21+
'basics',
22+
'srp',
23+
'ocp',
24+
'lsp',
25+
'lsp',
26+
'dip',
27+
])
28+
->map(fn ($file) => \Illuminate\Support\Str::of($file)->start('solid/'))
29+
->map(fn ($file) => new \App\Library($file));
30+
@endphp
31+
32+
@include('particles.library-section', ['sections' => $sections])
33+
34+
@endsection

routes/web.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@
5555
Route::view('/library/security', 'library.security')->name('library.security');
5656
Route::view('/library/how-to-ask', 'library.how-to-ask')->name('library.how-to-ask');
5757
Route::view('/library/collection', 'library.collection')->name('library.collection');
58-
58+
Route::view('/library/solid', 'library.solid')->name('library.solid');
5959
/*
6060
|--------------------------------------------------------------------------
6161
| Open Quiz

storage/library/solid/basics.md

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
---
2+
title: "Введение"
3+
description: "<strong>SOLID</strong> - это акроним для первых пяти принципов объектно-ориентированного проектирования от Роберта Мартина."
4+
---
5+
6+
*SOLID* - это основа объектно-ориентированного проектирования (ООП), которая помогает создавать гибкие, расширяемые и
7+
поддерживаемые программы. Когда ты понимаешь эти принципы, ты можешь писать код, который легко изменять и дополнять.
8+
9+
Эти принципы не только полезны для написания качественного кода, но и часто встречаются на собеседованиях на должности
10+
разработчика. Рекрутеры и работодатели ценят знание SOLID, так как это указывает на твою способность создавать
11+
высококачественное программное обеспечение.
12+
13+
Понимание SOLID помогает избежать типичных проблем, таких как сложная поддержка кода, неожиданные ошибки и сложности в
14+
расширении функциональности. Поэтому освоение этих принципов не только делает тебя лучшим разработчиком, но и помогает
15+
тебе быть востребованным на рынке труда.

storage/library/solid/dip.md

+71
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
---
2+
title: "Принцип инверсии зависимостей"
3+
description: "Любые более высокие (дочерние) классы всегда должны зависеть от абстракций, а не от деталей."
4+
---
5+
6+
Принцип инверсии зависимостей (Dependency Inversion Principle, DIP) гласит, что модули высокого уровня не должны
7+
зависеть от модулей низкого уровня, а оба типа модулей должны зависеть от абстракций. Также он утверждает, что
8+
абстракции не должны зависеть от деталей, а детали должны зависеть от абстракций.
9+
10+
Давай разберемся, что это означает на примере:
11+
12+
```php
13+
<?php
14+
// Нарушение принципа инверсии зависимостей
15+
class Mailer
16+
{
17+
18+
}
19+
20+
class SendWelcomeMessage
21+
{
22+
private $mailer;
23+
24+
public function __construct(Mailer $mailer)
25+
{
26+
$this->mailer = $mailer;
27+
}
28+
}
29+
```
30+
31+
Здесь класс SendWelcomeMessage зависит от конкретной реализации Mailer, что нарушает принцип инверсии зависимостей. Если
32+
мы захотим изменить способ отправки сообщений, нам придется изменять и класс SendWelcomeMessage.
33+
34+
Для исправления этой проблемы мы можем использовать абстракцию в виде интерфейса Mailer:
35+
36+
```php
37+
interface Mailer
38+
{
39+
public function send();
40+
}
41+
42+
class SmtpMailer implements Mailer
43+
{
44+
public function send()
45+
{
46+
// Реализация отправки через SMTP
47+
}
48+
}
49+
50+
class SendGridMailer implements Mailer
51+
{
52+
public function send()
53+
{
54+
// Реализация отправки через SendGrid
55+
}
56+
}
57+
58+
class SendWelcomeMessage
59+
{
60+
private $mailer;
61+
62+
public function __construct(Mailer $mailer)
63+
{
64+
$this->mailer = $mailer;
65+
}
66+
}
67+
```
68+
69+
Теперь класс SendWelcomeMessage зависит от абстракции Mailer, а не от конкретной реализации. Мы можем легко изменить
70+
способ отправки сообщений, просто передавая нужную реализацию интерфейса Mailer в конструктор SendWelcomeMessage. Это
71+
соответствует принципу инверсии зависимостей.

storage/library/solid/isp.md

+119
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
---
2+
title: "Принцип разделения интерфейса"
3+
description: "Интерфейсов должно быть много."
4+
---
5+
6+
Принцип разделения интерфейса (Interface Segregation Principle, ISP) предписывает, что клиенты не должны зависеть от
7+
методов, которые они не используют. Вместо этого интерфейсы должны быть разделены на более мелкие, специализированные
8+
интерфейсы, чтобы клиенты могли реализовывать только те методы, которые им нужны.
9+
10+
Давай разберем, как это работает на примере:
11+
12+
```php
13+
// Нарушение принципа разделения интерфейса
14+
interface Workable
15+
{
16+
public function canCode();
17+
public function code();
18+
public function test();
19+
}
20+
21+
class Programmer implements Workable
22+
{
23+
public function canCode()
24+
{
25+
return true;
26+
}
27+
28+
public function code()
29+
{
30+
return 'coding';
31+
}
32+
33+
public function test()
34+
{
35+
return 'testing in localhost';
36+
}
37+
}
38+
39+
class Tester implements Workable
40+
{
41+
public function canCode()
42+
{
43+
return false;
44+
}
45+
46+
public function code()
47+
{
48+
throw new Exception('Opps! I can not code');
49+
}
50+
51+
public function test()
52+
{
53+
return 'testing in test server';
54+
}
55+
}
56+
57+
class ProjectManagement
58+
{
59+
public function processCode(Workable $member)
60+
{
61+
if ($member->canCode()) {
62+
$member->code();
63+
}
64+
}
65+
}
66+
```
67+
68+
В этом примере интерфейс Workable содержит методы canCode(), code() и test(). Проблема в том, что не все классы,
69+
реализующие этот интерфейс, могут выполнять все эти действия. Например, класс Tester не может кодировать, но он должен
70+
реализовать метод code(), потому что интерфейс Workable требует это.
71+
72+
Чтобы исправить это, мы можем разделить интерфейс на более мелкие и специализированные интерфейсы:
73+
74+
```php
75+
// Улучшенный вариант
76+
interface Codeable
77+
{
78+
public function code();
79+
}
80+
81+
interface Testable
82+
{
83+
public function test();
84+
}
85+
86+
class Programmer implements Codeable, Testable
87+
{
88+
public function code()
89+
{
90+
return 'coding';
91+
}
92+
93+
public function test()
94+
{
95+
return 'testing in localhost';
96+
}
97+
}
98+
99+
class Tester implements Testable
100+
{
101+
public function test()
102+
{
103+
return 'testing in test server';
104+
}
105+
}
106+
107+
class ProjectManagement
108+
{
109+
public function processCode(Codeable $member)
110+
{
111+
$member->code();
112+
}
113+
}
114+
```
115+
116+
Теперь интерфейсы Codeable и Testable более специализированы. Класс Programmer реализует оба интерфейса, потому что он
117+
может кодировать и тестировать. Класс Tester реализует только интерфейс Testable, потому что он может только
118+
тестировать. Таким образом, классы могут реализовывать только те методы, которые им нужны, что соответствует принципу
119+
разделения интерфейса.

storage/library/solid/lsp.md

+89
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
---
2+
title: "Принцип подстановки Барбары Лисков"
3+
description: "Дочерние классы должны работать так, что бы ими можно было заменить родительские."
4+
---
5+
6+
Принцип подстановки Барбары Лисков (Liskov Substitution Principle, LSP) утверждает, что поведение подклассов должно быть
7+
совместимо с поведением их суперклассов. Другими словами, объекты подтипов должны быть заменяемыми экземплярами своих
8+
супертипов без изменения ожидаемого поведения программы. Давай разберемся с примером, чтобы лучше понять этот принцип:
9+
10+
```php
11+
<?php
12+
13+
// Нарушение принципа подстановки Барбары Лисков
14+
// Проблема квадрата и прямоугольника
15+
class Rectangle
16+
{
17+
protected $width;
18+
protected $height;
19+
20+
public function setHeight($height)
21+
{
22+
$this->height = $height;
23+
}
24+
25+
public function getHeight()
26+
{
27+
return $this->height;
28+
}
29+
30+
public function setWidth($width)
31+
{
32+
$this->width = $width;
33+
}
34+
35+
public function getWidth()
36+
{
37+
return $this->width;
38+
}
39+
40+
public function area()
41+
{
42+
return $this->height * $this->width;
43+
}
44+
}
45+
46+
class Square extends Rectangle
47+
{
48+
public function setHeight($value)
49+
{
50+
$this->width = $value;
51+
$this->height = $value;
52+
}
53+
54+
public function setWidth($value)
55+
{
56+
$this->width = $value;
57+
$this->height = $value;
58+
}
59+
}
60+
61+
class RectangleTest
62+
{
63+
private $rectangle;
64+
65+
public function __construct(Rectangle $rectangle)
66+
{
67+
$this->rectangle = $rectangle;
68+
}
69+
70+
public function testArea()
71+
{
72+
$this->rectangle->setHeight(2);
73+
$this->rectangle->setWidth(3);
74+
// Ожидаем, что площадь прямоугольника будет 6
75+
}
76+
}
77+
```
78+
79+
В данном примере класс Square наследуется от Rectangle, что кажется логичным, так как квадрат является частным случаем
80+
прямоугольника. Однако, нарушается принцип подстановки Барбары Лисков из-за того, что Square переопределяет методы
81+
setHeight() и setWidth() так, чтобы они всегда делали высоту равной ширине.
82+
83+
Что делает этот пример нарушением LSP? Дело в том, что ожидается, что при вызове setHeight() и setWidth() объекта
84+
Rectangle сначала будет изменяться одно измерение, а потом другое. Однако в случае Square эти методы нарушают это
85+
ожидание, что может привести к непредсказуемому поведению в программах, которые используют Rectangle или его подтипы.
86+
87+
Как исправить это? Один из способов - пересмотреть архитектуру классов так, чтобы Square не наследовался от Rectangle,
88+
так как это нарушает LSP. Вместо этого, можно использовать композицию или выделить общий интерфейс для обоих классов и
89+
разработать их независимо.

0 commit comments

Comments
 (0)