Skip to content

Commit 0b01789

Browse files
authored
Added section "Getters and Setters" into library (#279)
* Added section "Getters and Setters" into library * Fixed ambiguous or incorrect remarks * Added route endpoint
1 parent 46dae49 commit 0b01789

File tree

6 files changed

+244
-0
lines changed

6 files changed

+244
-0
lines changed

public/img/ui/bone.svg

Lines changed: 41 additions & 0 deletions
Loading
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
@extends('layout')
2+
@section('title', 'Геттеры и сеттеры')
3+
@section('description', 'Разрушают инкапсуляцию, увеличивают связанность кода и приводят к процедурному стилю программирования.')
4+
@section('content')
5+
6+
<x-header align="align-items-center">
7+
<x-slot name="sup">Объектно-ориентированное программирование</x-slot>
8+
<x-slot name="title">Геттеры и сеттеры в объектах</x-slot>
9+
<x-slot name="description">
10+
Разрушают инкапсуляцию, увеличивают связанность кода и приводят к процедурному стилю программирования.
11+
</x-slot>
12+
<x-slot name="content">
13+
<img src="/img/ui/bone.svg" class="img-fluid d-block mx-auto">
14+
</x-slot>
15+
</x-header>
16+
17+
@php
18+
$sections = collect([
19+
'basics',
20+
'getters-and-setters',
21+
'tell-dont-ask',
22+
])
23+
->map(fn ($file) => \Illuminate\Support\Str::of($file)->start('getters-and-setters/'))
24+
->map(fn ($file) => new \App\Library($file));
25+
@endphp
26+
27+
@include('particles.library-section', ['sections' => $sections])
28+
@endsection

routes/web.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@
6565
Route::view('/library/collection', 'library.collection')->name('library.collection');
6666
Route::view('/library/solid', 'library.solid')->name('library.solid');
6767
Route::view('/library/actions', 'library.actions')->name('library.actions');
68+
Route::view('/library/getters-and-setters', 'library.getters-and-setters')->name('library.getters-and-setters');
6869
/*
6970
|--------------------------------------------------------------------------
7071
| Open Quiz
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
---
2+
title: "Основы"
3+
description: "В программировании существует разные подходы к организации кода."
4+
---
5+
6+
В программировании существует множество разных подходов к организации кода. Например знакомый со школьной скамьи или
7+
университета процедурный стиль, когда программа описывается серией последовательных инструкций: «сделай 1, сделай 2,
8+
сделай 3».
9+
10+
```php
11+
step1($data);
12+
step2($data);
13+
step3($data);
14+
```
15+
16+
Этот стиль программирования прост в освоении и долгие годы был основным в программировании в C, Pascal и других языках.
17+
18+
Однако в современном PHP и Laravel обычно применяется объектно-ориентированный подход. В котором мы создаём классы,
19+
инициируем их и используем методы, чтобы управлять поведением объектов. ООП основывается на идее, что данные и поведение
20+
должны быть объединены в единый «живой» объект. Например, представьте класс `App`:
21+
22+
```php
23+
$app = new App();
24+
25+
$app->start();
26+
```
27+
28+
В этом случае мы не указываем пошагово, что необходимо делать, а поручаем объекту выполнить всю необходимую
29+
инициализацию и запуск (инкапсуляция). Такой подход позволяет скрыть детали реализации и предоставляет лишь интерфейс
30+
для взаимодействия с объектом.
31+
32+
Но существует чертовски много кода и практик на ООП языках, который является процедурным по своей конструкции. Поэтому
33+
мы разберем несколько примеров.
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
---
2+
title: "Глупые объекты рождают процедурные участки кода"
3+
description: "Объект как контейнер для данных."
4+
---
5+
6+
Встречается практика, когда один объект используется исключительно как контейнер для данных – он
7+
не содержит бизнес-логики, а только хранит информацию. Рассмотрим пример:
8+
9+
```php
10+
class User
11+
{
12+
private string $name;
13+
private string $email;
14+
15+
public function getName(): string
16+
{
17+
return $this->name;
18+
}
19+
20+
public function setName(string $name): void
21+
{
22+
$this->name = $name;
23+
}
24+
25+
public function getEmail(): string
26+
{
27+
return $this->email;
28+
}
29+
30+
public function setEmail(string $email): void
31+
{
32+
$this->email = $email;
33+
}
34+
}
35+
```
36+
37+
> {note} Часто такой класс ошибочно называют "Data Transfer Object" (DTO), но это не так. DTO должен быть иммутабельным, а наличие сеттеров нарушает этот принцип.
38+
39+
Здесь объект `User` используется только для хранения данных.
40+
Дальше эти данные обрабатываются в различных частях приложения. Например:
41+
42+
```php
43+
// В одном участке кода создаем объект и заполняем его данными
44+
$user = new User();
45+
$user->setName('John Doe');
46+
$user->setEmail('john.doe@example.com');
47+
48+
// В другом участке кода валидируем данные
49+
$validator = new Validator();
50+
$validator->validate($user);
51+
52+
// В третьем участке кода отправляем письмо пользователю
53+
$notification = new WelcomeNotification();
54+
$notification->send($user->getEmail());
55+
```
56+
57+
В этом примере мы видим, что объект `User` не обладает собственной логикой – за обработку данных отвечают внешние
58+
классы, такие как `Validator` и `Mailer`. По сути, вместо использования массива для хранения информации мы применяем
59+
объект, но бизнес-логика вынесена за его пределы. Это приближает архитектуру к процедурному стилю, где программа состоит
60+
из набора функций, выполняющих действия над данными.
61+
62+
Аналогичная ситуация наблюдается и в `ORM`, которые не используют подход `Active Record`, например, в `Doctrine`:
63+
64+
```php
65+
$entityManager = EntityManager::create($connection, $config);
66+
67+
// Создаем объект пользователя
68+
$user = new User();
69+
$user->setName("John Doe");
70+
$user->setEmail("john.doe@example.com");
71+
72+
// Сохраняем в БД
73+
$entityManager->persist($user);
74+
$entityManager->flush();
75+
```
76+
77+
Это противоречит принципам объектно-ориентированного программирования, так как объекты должны быть ответственными за
78+
свои данные и поведение.
79+
80+
В результате мы получаем систему, где логика распределена между «глупыми» объектами и процедурными участками кода. Это
81+
затрудняет понимание, тестирование и дальнейшее развитие приложения, так как в нем присутствует неявное разделение
82+
ответственности.
83+
84+
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
---
2+
title: "Tell, Don’t Ask"
3+
description: "Объекты должны выполнять действия, а не только отдавать свои данные"
4+
---
5+
6+
<mark>Tell, Don’t Ask</mark> – это один из хорошо сформулированных принципов качественного объектно-ориентированного проектирования, который помогает создать чистую и легко поддерживаемую архитектуру.
7+
8+
> «Не спрашивай объект о данных, чтобы принять решение – скажи объекту, что делать.»
9+
10+
На первый взгляд этот принцип может показаться противоречащим принципу единой ответственности (SRP), но в реальной разработке зачастую приходится делать выбор, какой из принципов важнее в конкретном контексте. Тем не менее, чем больше принципов удается соблюсти одновременно, тем лучше.
11+
12+
Вместо того чтобы создавать «глупый» объект, мы можем инкапсулировать бизнес-логику прямо в объекте или в связанном сервисе. Например, если нам необходимо валидировать и отправить приветственное письмо, можно поручить это объекту или сервису, который знает, как с ним работать. Рассмотрим два варианта:
13+
14+
Вместо того чтобы извлекать данные и проверять их во внешнем коде, объект сам выполняет необходимое действие.
15+
16+
```php
17+
class User
18+
{
19+
protected string $name;
20+
protected string $email;
21+
22+
private function validate(): bool
23+
{
24+
$validator = Validator::make([
25+
'name' => $this->name,
26+
'email' => $this->email,
27+
], [
28+
'name' => 'required',
29+
'email' => 'required|email',
30+
]);
31+
32+
return $validator->passes()
33+
}
34+
35+
public function register(): void
36+
{
37+
$this->validate();
38+
39+
// Сохранение пользователя в БД...
40+
41+
$this->notify(WelcomeNotification::class);
42+
}
43+
}
44+
45+
$user = new User(
46+
name: "John Doe",
47+
email: "john.doe@example.com"
48+
);
49+
50+
$user->register();
51+
```
52+
53+
Здесь объект сам выполняет проверку и отправляет уведомление, а не предоставляет данные для обработки извне.
54+
55+
> {tip} К тому же принцип **Tell, Don’t Ask** хорошо сочетается с **Законом Деметры** (*Law of Demeter, LoD*), который также способствует созданию более устойчивых и независимых объектов.
56+
57+
Можно узнать больше из работ [Мартина Фаулера](https://martinfowler.com/bliki/TellDontAsk.html), [Аллена Холуба](https://www.infoworld.com/article/2161183/why-getter-and-setter-methods-are-evil.html) и [Егора Бугаенко](https://www.yegor256.com/2014/09/16/getters-and-setters-are-evil.html).

0 commit comments

Comments
 (0)