Skip to content

Commit

Permalink
typos
Browse files Browse the repository at this point in the history
  • Loading branch information
Yan S committed Feb 14, 2020
1 parent 9e3ebb6 commit 004c342
Show file tree
Hide file tree
Showing 5 changed files with 24 additions and 24 deletions.
4 changes: 2 additions & 2 deletions manuscript/2-di.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
## Принцип Единственной Ответственности

Вы, возможно, слышали о Принципе Единственной Ответственности (Single Responsibility Principle, SRP).
Одно из его определений: Каждый модуль, класс или функция должны иметь ответсвенность над единой частью функционала.
Одно из его определений: Каждый модуль, класс или функция должны иметь ответственность над единой частью функционала.
Много разработчиков упрощают его до "класс должно делать что-то одно", но это определение не самое удачное.
Роберт Мартин предложил другое определение, где заменил слово "ответственность" на "причину для изменения": "Класс должен иметь лишь одну причину для изменения".
"Причина для изменения" более удобный термин и мы можем начать рассуждать об архитектуре используя его.
Expand Down Expand Up @@ -1635,7 +1635,7 @@ class WantsToDispatchJobs(private val dispatcher: Dispatcher)
}
```

Синтаксис PHP является некоторым барьером для использования DI, я надеюсь скоро это будет нивелировано либо изменениями в синтаксисе, либо иструментами в средах разработки.
Синтаксис PHP является некоторым барьером для использования DI, я надеюсь скоро это будет нивелировано либо изменениями в синтаксисе, либо инструментами в средах разработки.

После нескольких лет использования и неиспользования трейтов я могу сказать, что разработчики используют трейты по двум причинам:

Expand Down
2 changes: 1 addition & 1 deletion manuscript/4-application-layer.md
Original file line number Diff line number Diff line change
Expand Up @@ -468,7 +468,7 @@ final class PublishPostCommand
}
}
```
В шаблоне **Command Bus** суффикс**Command** используется для DTO-классов, а классы испольняющие команды называются **CommandHandler**.
В шаблоне **Command Bus** суффикс**Command** используется для DTO-классов, а классы исполняющие команды называются **CommandHandler**.

```php
final class ChangeUserPasswordCommand
Expand Down
22 changes: 11 additions & 11 deletions manuscript/5-error-handling.md
Original file line number Diff line number Diff line change
Expand Up @@ -362,7 +362,7 @@ class Handler extends ExceptionHandler
{
if ($this->shouldReport($e))
{
// Это отличнео место для
// Это отличное место для
// интеграции сторонних сервисов
// для мониторинга ошибок
}
Expand Down Expand Up @@ -459,7 +459,7 @@ public class Foo
}
}
```
Этот код даже не скопилируется.
Этот код даже не скомпилируется.
Сообщение компилятора: "Error:(5, 9) java: unreported exception java.lang.Exception; must be caught or declared to be thrown"
Есть два способа исправить это. Поймать его:

Expand Down Expand Up @@ -508,7 +508,7 @@ public class FooCaller
}
```
Разумеется, работать так с **каждым** исключение будет той еще пыткой.
В Java есть **проверяемые** исключения, которые обязаны быть пойманы или обьявлены в сигнатуре, и **непроверяемые**, которые могут быть выброшены без всяких дополнительных условий.
В Java есть **проверяемые** исключения, которые обязаны быть пойманы или объявлены в сигнатуре, и **непроверяемые**, которые могут быть выброшены без всяких дополнительных условий.
Взглянем на корень дерева классов исключений в Java (PHP, начиная с седьмой версии, имеет абсолютно такое же):

```
Expand All @@ -531,7 +531,7 @@ public class File
```
Что сигнатура метода **getCanonicalPath** говорит разработчику?
Там нет никаких параметров, возвращает строку, может выбросить исключение **IOException** а также любое непроверяемое исключение.
Возвращаесь к двум типам ошибок:
Возвращаясь к двум типам ошибок:

1. Ошибки, которые понятны вызывающему коду и могут быть эффективно обработаны там
2. Другие ошибки
Expand All @@ -542,7 +542,7 @@ public class File
Все это приводит к более корректной обработке ошибок.

Хорошо, в Java это есть, в PHP - нет.
Почему я все ещё об этом гооврю?
Почему я все ещё об этом говорю?
IDE, которое я использую, PhpStorm, имитирует поведения Java.

```php
Expand All @@ -555,7 +555,7 @@ class Foo
}
```
PhpStorm подсветит 'throw new Exception();' с предупреждением: 'Unhandled Exception'.
И есть два пути исбавиться от этого:
И есть два пути избавиться от этого:

1. Поймать исключение
2. Описать его в тэге @throws phpDoc-комментария метода:
Expand All @@ -573,7 +573,7 @@ class Foo
}
```

Список непроверямых классов конфигурируется.
Список непроверяемых классов конфигурируется.
По умолчанию он выглядит так: **\Error**, **\RuntimeException** and **\LogicException**.
Их можно выбрасывать не опасаясь предупреждений.

Expand Down Expand Up @@ -620,13 +620,13 @@ final class UserController
{
$service->changePassword($request->getDto());

// возаращем успешный ответ
// возвращаем успешный ответ
}
}
```
Не очень удобно. Даже если учесть, что PhpStorm умеет генерировать все эти тэги автоматически.
Возвращаясь к нашему неидеальнмоу миру: Класс **ModelNotFoundException** в Laravel уже отнаследован от **\RuntimeException**.
Соответственно, он непроверямый по умолчанию.
Возвращаясь к нашему неидеальному миру: Класс **ModelNotFoundException** в Laravel уже отнаследован от **\RuntimeException**.
Соответственно, он непроверяемый по умолчанию.
Это имеет смысл, поскольку глубоко внутри собственного обработчика ошибок Laravel обрабатывает эти исключения сам.
Поэтому, в нашем текущем положении, стоит тоже пойти на такую сделку с совестью:

Expand All @@ -642,7 +642,7 @@ class BusinessException extends \RuntimeException
"throws Exception" вообще не дает никакой полезной информации.
Это просто заставляет клиентский код повторять этот бесполезный "throws Exception" в своей сигнатуре.

Я вернусь к исключениям в главе про Доменный слой, когда этот подход непроверямыми исключениями станет не очень удобным.
Я вернусь к исключениям в главе про Доменный слой, когда этот подход непроверяемыми исключениями станет не очень удобным.

## Пара слов в конце главы

Expand Down
6 changes: 3 additions & 3 deletions manuscript/7-events.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ final class PollService
Объект опроса непростой.
Он абсолютно бесполезен без опций ответа.
Мы должны позаботиться о его консистентности.
Этот маленький промежутор времени, когда мы уже создали объект опроса и добавили его в базу данных, но еще не создали его опции ответа, очень опасен!
Этот маленький промежуток времени, когда мы уже создали объект опроса и добавили его в базу данных, но еще не создали его опции ответа, очень опасен!

## Database transactions

Expand Down Expand Up @@ -270,7 +270,7 @@ final class EventServiceProvider extends ServiceProvider
Теперь Слой Приложения просто сообщает, что был создан новый опрос (событие **PollCreated**).
Приложение имеет конфигурацию для реагирования на события.
Классы слушателей событий (**Listener**) содержат реакцию на события.
Интерфейс **ShouldQueue** работает точно также, сообщая о том, когда должно быть запущено выполнение этого слушателя: сразу же или в отложенно.
Интерфейс **ShouldQueue** работает точно также, сообщая о том, когда должно быть запущено выполнение этого слушателя: сразу же или в отложено.
События - очень мощная вещь, но здесь тоже есть ловушки.

## Использование событий Eloquent
Expand Down Expand Up @@ -345,7 +345,7 @@ public function create(PollCreateDto $request)
А самое страшное, что событие вроде **PollCreated**, может быть вызвано, но дальше в транзакции будет ошибка и вся она будет откачена назад.
Письмо же пользователю об опросе, который даже не создался, все равно будет отправлено.
Я нашел пару пакетов для Laravel, которые ловят все эти события, хранят их временно, и выполняют только после того, как транзакция будет успешно завершена (гуглите "Laravel transactional events").
Да, они решают множество из этих проблем, но всё это выглядит так нестественно!
Да, они решают множество из этих проблем, но всё это выглядит так неестественно!
Простая идея генерировать нормальное бизнес-событие **PollCreated** после успешной транзакции намного лучше.

## Сущности как поля классов-событий
Expand Down
14 changes: 7 additions & 7 deletions manuscript/8-unit-test.md
Original file line number Diff line number Diff line change
Expand Up @@ -371,7 +371,7 @@ class OrderService

Но если мы взглянем на класс **OrderService**, то увидим, что **TaxCalculator** не выглядит его зависимостью.
Он не выглядит как что-то внешнее, нужное **OrderService** для работы.
Оне выглядит как часть класса **OrderService**.
Он выглядит как часть класса **OrderService**.


![](images/unit.png)
Expand Down Expand Up @@ -402,7 +402,7 @@ Unit-тесты могут тестировать класс **OrderService** н
Модуль - весьма широкое понятие.
В начале этой статьи модулем была маленькая функция, а иногда в модуле может содержаться несколько классов.
Программные объекты внутри модуля должны быть сфокусированы на одной ответственности, другими словами, иметь сильную связность.
Когда методы класса полностью независмы друг от друга, класс не является модулем.
Когда методы класса полностью независимы друг от друга, класс не является модулем.
Каждый метод класса - это модуль в данном случае.
Возможно, стоит вынести эти методы в отдельные классы, чтобы разработчики не просматривали кучу лишнего кода каждый раз?
Помните я говорил, что часто предпочитаю классы одной команды, такие как **PublishPostCommand**, а не **PostService** классы?
Expand All @@ -412,7 +412,7 @@ Unit-тесты могут тестировать класс **OrderService** н

Обычно зависимость - это интерфейс, который имеет несколько реализаций.
Использование реальных реализаций этого интерфейса во время unit-тестирования - плохая идея, поскольку там могут проводиться те самые операции ввода-вывода, замедляющие тестирование и не дающие провести тестирование этого модуля в изоляции.
Прогон unit-тестов должден быть быстр как молния, поскольку запускаться они будут часто и важно, чтобы разработчик запустив их не потерял фокус над кодом.
Прогон unit-тестов должен быть быстр как молния, поскольку запускаться они будут часто и важно, чтобы разработчик запустив их не потерял фокус над кодом.
Написал код - прогнал тесты, еще написал код - прогнал тесты.
Быстрые тесты позволят ему оставаться более продуктивным, не позволяя отвлекаться.
Решение в лоб задачи изоляции класса от зависимостей - создание отдельной реализации этого интерфейса, предназначенного просто для тестирования.
Expand Down Expand Up @@ -626,7 +626,7 @@ class PostsTest extends TestCase
Просто добавив трейт **SoftDeletes** в класс **Post**, мы уроним этот тест.
Однако, приложение работает абсолютно также, выполняет те же самые требования и пользователи этой разницы не заметят.
Функциональные тесты не должны падать в таких условиях.
Тест, который делает запрос в приожение, а потом лезет в базу данных проверять результат, не является настоящий функциональным тестом.
Тест, который делает запрос в приложение, а потом лезет в базу данных проверять результат, не является настоящий функциональным тестом.
Он знает слишком многое про то, как работает приложение, как оно хранит данные и какие таблицы для этого использует.
Это еще один пример тестирования методом белого ящика.

Expand Down Expand Up @@ -661,7 +661,7 @@ class PostsTest extends TestCase

public function testDelete()
{
// Здесь некоторая иницизация, чтобы создать
// Здесь некоторая инициализация, чтобы создать
// объект Post с id = $postId

// Удостоверяемся, что этот объект есть
Expand All @@ -679,7 +679,7 @@ class PostsTest extends TestCase
}
```

Этому тесту абсолютно все равно как удлаен объект, с помощью 'delete' SQL запроса или с помощью Soft delete шаблона.
Этому тесту абсолютно все равно как удален объект, с помощью 'delete' SQL запроса или с помощью Soft delete шаблона.
Функциональный тест проверяет поведение приложения в целом.
Ожидаемое поведение, если объект удален - он не возвращается по своему id и тест проверяет именно это.

Expand Down Expand Up @@ -1088,7 +1088,7 @@ class PollServiceTest extends \PHPUnit\Framework\TestCase
Время от времени, менеджер или разработчики открывают приложение, выполняют в нём некоторые действия, проверяя корректность его работы и насколько красив его интерфейс.
Это неавтоматическое тестирование без какой-либо стратегии.

Дальше (обычно после каких-нибудь болезенных ошибок на продакшене) команда решает что-то изменить.
Дальше (обычно после каких-нибудь болезненных ошибок на продакшене) команда решает что-то изменить.

Первое, очевидное, решение - нанять ручного тестировщика.
Он может описать главные сценарии работы с приложением и после каждого обновления проходить по этим сценариям, проверяя, что приложение работает как требуется (это называется регрессионное тестирование).
Expand Down

0 comments on commit 004c342

Please sign in to comment.