Чистый код Глотова Марина Начальник отдела информационных разработок
2 2. Как загрязняется код
3 3. Как загрязняется код
4 4. Код-монстр
Зачем нужен чистый код?
6 6. Несколько фактов а разработке
7 Над чтением кода мы проводим в 10 раз больше времени, чем над его написанием 7. Несколько фактов а разработке
8 Над чтением кода мы проводим в 10 раз больше времени, чем над его написанием 8. Несколько фактов а разработке Чем больше загрязняется наш код, тем больше мы теряем в производительности время производительность
9 9. Что даёт чистый код?
Что даёт чистый код? Быстрое чтение и понимание кода.
Что даёт чистый код? Быстрое чтение и понимание кода. Уменьшение количества багов.
Что даёт чистый код? Быстрое чтение и понимание кода. Уменьшение количества багов. Простые модульные тесты.
Стандарты оформления кода
Стандарты оформления кода PSR (PHP Standards Recommendations):
Стандарты оформления кода PSR (PHP Standards Recommendations): PSR-2
Стандарты оформления кода PSR (PHP Standards Recommendations): PSR-2 PSR-4
Стандарты оформления кода PSR (PHP Standards Recommendations): PSR-2 PSR-4 PSR-5
Стандарты оформления кода PSR (PHP Standards Recommendations): PSR-2 PSR-4 PSR-5 phpDocumentor
Рефакторинг
Архитектура кода MVC
Тонкий и толстый контроллер
Тонкий и толстый контроллер protected function actionDelete() { if (!Request::isAjaxRequest()) { $this->renderError(); } $payment_id = Request::getInt('id', $_POST); if (!$payment_id) { $this->returnJsonResponse(['success' => false, 'msg' => 'Платёж не найден']); } $payment = PaymentModel::model(false)->findByPk($payment_id); if (null === $payment) { $this->returnJsonResponse(['success' => false, 'msg' => 'Платёж не найден']); } $result = $payment->deletePayment(); if ($result) { PaymentsHelper::i()->updateFirmProperties((int)$payment->firm_id); $this->returnJsonResponse(['success' => true, 'msg' => 'Платёж удалён']); } $this->returnJsonResponse(['success' => false, 'msg' => 'Ошибка удаления платежа']); }
Дублирование кода public function transferToOrganizer(int $firm_id): bool { какая-то логика….. $result = $this->getFund($firm_id) ->spend($this->getComment(Trade::COMMENT_TO_ORGANIZER)); какая-то логика….. } public function transferToParticipant(int $firm_id): bool { какая-то логика….. $result = $this->getFund($firm_id) ->spend($this->getComment(Trade::COMMENT_TO_PARTICIPANT)); какая-то логика….. }
Дублирование кода public function transfer (int $firm_id, int $comment_type): bool { какая-то логика….. $result = $this->getFund($firm_id) ->spend($this->getComment($comment_type)); какая-то логика….. }
Длинный метод, класс
Длинный метод, класс Умеет делать всё
Длинный метод, класс Умеет делать всё Часто содержит дублирование кода
Длинный метод, класс Умеет делать всё Часто содержит дублирование кода Разбиваем
Много параметров public function transfer (int $firm_id, int $comment_type, ….): bool { какая-то логика….. $result = $this->getFund($firm_id) ->spend($this->getComment($comment_type)); какая-то логика….. }
Много параметров class Operation { private $firm_id; public function setFirmId(int $firm_id): self { $this->firm_id = $firm_id; return $this; } private function getFirmId() { return $this->firm_id; } public function transfer(int $comment_type): bool { //какая - то логика….. $result = $this->getFund($this->getFirmId()) ->spend($this->getComment($comment_type)); //какая - то логика….. }
Завистливые функции public function transfer(Comment $comment): bool { //какая - то логика… $result = $this->getFund($this->getFirmId()) ->spend($this->getComment($comment_type)); $notification = new Notification(); $notification->date = time(); $notification->text = 'some text'; $notification->to_whom = $this->getFirm()->getMainUser(); $notification->send(); }
Завистливые функции public function transfer(Comment $comment): bool { //какая - то логика… $result = $this->getFund($this->getFirmId()) ->spend($this->getComment($comment_type)); $notification = new Notification(); $notification->date = time(); $notification->text = 'some text'; $notification->to_whom = $this->getFirm()->getMainUser(); $notification->send(); } public function transfer(Comment $comment): bool { //какая - то логика… $result = $this->getFund($this->getFirmId()) ->spend($this->getComment($comment_type)); $notification = (new Notification()) ->setText('some text') ->setUserId($this->getFirm()->getMainUser()) ->send(); }
Метод возвращает несколько типов public function getActivePaymentIds(): ?array { $all_payments = $this->getAllPayments(); if (empty($all_payments)) { return null; } $active_payments = []; foreach ($all_payments as $payment) { //выбираем активные платежи } return $active_payments; }
Метод возвращает несколько типов public function getActivePaymentIds(): ?array { $all_payments = $this->getAllPayments(); if (empty($all_payments)) { return null; } $active_payments = []; foreach ($all_payments as $payment) { //выбираем активные платежи } return $active_payments; } public function getActivePaymentIds(): array { $all_payments = $this->getAllPayments(); if (empty($all_payments)) { return []; } $active_payments = []; foreach ($all_payments as $payment) { //выбираем активные платежи } return $active_payments; }
Область видимости свойств и методов Делайте публичными методы, а не данные для работы этих методов: class Button() { private $is_enabled = true; public function setEnabled(bool $is_enabled): self { $this->is_enabled = $is_enabled; return $this; }
Область видимости свойств и методов Делайте публичными методы, а не данные для работы этих методов: class Button() { private $is_enabled = true; public function setEnabled(bool $is_enabled): self { $this->is_enabled = $is_enabled; return $this; } class Button() { private $is_enabled = true; public function enable() { $this->is_enabled = true; return $this; } public function disable() { $this->is_enabled = false; return $this; }
//TODO // todo этот метод немного вводит в ступор, // т.к. есть метод getConcludedStatuses /** * Список статусов для подписанного документа array */ public static function getSignedStatuses() { return [ self::STATUS_SIGNED_BY_PARTICIPANT, self::STATUS_SUSPENDED, self::STATUS_SIGNED_BY_BOTH_SIDES, ]; }
Что ухудшает читаемость кода?
Плохое именование методов и переменных ТРУДНЕЕ ПРИДУМЫВАНИЯ НАЗВАНИЯ ПЕРЕМЕННОЙ ЯВЛЯЕТСЯ ТОЛЬКО ПРИДУМЫВАНИЕ НАЗВАНИЯ ДЛЯ КОММИТА
Основные принципы именования сущностей Используем английский язык, нет транслитерации.
Основные принципы именования сущностей Используем английский язык, нет транслитерации. Стиль переменной свободный ($snake_case, $camelCase, $StudlyCase). Для метода это camelCase, для класса – StudlyCase.
Основные принципы именования сущностей Используем английский язык, нет транслитерации. Стиль переменной свободный ($snake_case, $camelCase, $StudlyCase). Для метода это camelCase, для класса – StudlyCase. Переменные – это существительные $user, методы – глаголы updateUserPermissions().
Основные принципы именования сущностей Используем английский язык, нет транслитерации. Стиль переменной свободный ($snake_case, $camelCase, $StudlyCase). Для метода это camelCase, для класса – StudlyCase. Переменные – это существительные $user, методы – глаголы updateUserPermissions() Переменная: одна сущность – единственное число $user, набор сущностей – множественное число $users.
Основные принципы именования сущностей Используем английский язык, нет транслитерации. Стиль переменной свободный ($snake_case, $camelCase, $StudlyCase). Для метода это camelCase, для класса – StudlyCase. Переменные – это существительные $user, методы – глаголы updateUserPermissions() Переменная: одна сущность – единственное число $user, набор сущностей – множественное число $users. Конкретика: да - $current_user, нет - $user.
Основные принципы именования сущностей Используем английский язык, нет транслитерации. Стиль переменной свободный ($snake_case, $camelCase, $StudlyCase). Для метода это camelCase, для класса – StudlyCase. Переменные – это существительные $user, методы – глаголы updateUserPermissions() Переменная: одна сущность – единственное число $user, набор сущностей – множественное число $users. Конкретика: да - $current_user, нет - $user. Является – isActive(), имеет – hasPermissions(), может – canUpload().
Основные принципы именования сущностей Используем английский язык, нет транслитерации. Стиль переменной свободный ($snake_case, $camelCase, $StudlyCase). Для метода это camelCase, для класса – StudlyCase. Переменные – это существительные $user, методы – глаголы updateUserPermissions() Переменная: одна сущность – единственное число $user, набор сущностей – множественное число $users. Конкретика: да - $current_user, нет - $user. Является – isActive(), имеет – hasPermissions(), может – canUpload(). Позитивный смысл: да – isActive(), нет – isNotActive().
Основные принципы именования сущностей Используем английский язык, нет транслитерации. Стиль переменной свободный ($snake_case, $camelCase, $StudlyCase). Для метода это camelCase, для класса – StudlyCase. Переменные – это существительные $user, методы – глаголы updateUserPermissions() Переменная: одна сущность – единственное число $user, набор сущностей – множественное число $users. Конкретика: да - $current_user, нет - $user. Является – isActive(), имеет – hasPermissions(), может – canUpload(). Позитивный смысл: да – isActive(), нет – isNotActive(). Возвращает – getUser(), устанавливает – setUser().
Основные принципы именования сущностей Используем английский язык, нет транслитерации. Стиль переменной свободный ($snake_case, $camelCase, $StudlyCase). Для метода это camelCase, для класса – StudlyCase. Переменные – это существительные $user, методы – глаголы updateUserPermissions() Переменная: одна сущность – единственное число $user, набор сущностей – множественное число $users. Конкретика: да - $current_user, нет - $user. Является – isActive(), имеет – hasPermissions(), может – canUpload(). Позитивный смысл: да – isActive(), нет – isNotActive(). Возвращает – getUser(), устанавливает – setUser(). Не сокращать: да - $number, нет - $num.
Основные принципы именования сущностей setDefaultAttributes() checkUserAndInitializeHisSession()
Основные принципы именования сущностей setDefaultAttributes() checkUserAndInitializeHisSession() checkUser() initializeUserSession()
Избыточные комментарии Когда писать комментарии: Краткое описание метода (по мере необходимости). Документация к методу.
Избыточные комментарии Когда писать комментарии: Краткое описание метода (по мере необходимости). Документация к методу. Пояснение нетривиальной бизнес-логики.
Избыточные комментарии Когда писать комментарии: Краткое описание метода (по мере необходимости). Документация к методу. Пояснение нетривиальной бизнес-логики. Пояснение параметров к методу (по мере необходимости).
Магия
Ранний выход из if foreach ($firms as $firm) { if ($firm->isActive()) { $result = $firm->updateStatus(); if ($result) { $this->registerEvent(); }
Ранний выход из if foreach ($firms as $firm) { if ($firm->isActive()) { $result = $firm->updateStatus(); if ($result) { $this->registerEvent(); } foreach ($firms as $firm) { if (!$firm->isActive()) { continue; } if ($firm->updateStatus()) { $this->registerEvent(); }
if-else-return if ($stuff === $another_stuff) { return true; } else { return false; }
if-else-return if ($stuff === $another_stuff) { return true; } else { return false; } return ($stuff === $another_stuff);
Инструменты
Инструменты Анализаторы кода: PhpInspector
Инструменты Анализаторы кода: PhpInspector Код-ревью: коммиты, phabricator, upsource
62 b2b-center.ru/jobs
Спасибо за внимание! Глотова Марина