Как найти вонючие части вашего кода [Часть XXV]

Как найти вонючие части вашего кода [Часть XXV]

1 ноября 2022 г.

Изображение cookie_studio на Freepik

Запахи кода — это классика.

Это пахнет, потому что, вероятно, есть много случаев, когда его можно отредактировать или улучшить.

n Большинство из этих запахов — всего лишь намеки на то, что что-то может быть не так. Они не обязаны исправляться как таковые… (Тем не менее, вам следует изучить это.)

Пахнет предыдущим кодом

Продолжим...


Code Smell 121 — Проверка строк

Вам необходимо проверить строки. Таким образом, вам вообще не нужны строки

<цитата>

TL;DR: поиск отсутствующих объектов домена при проверке строк.

Проблемы

  • Примитивная одержимость.
  • Ошибка биекции
  • Проверенные строки — это подмножество всех возможных строк.
  • Нарушение принципа
  • Fail Fast.
  • Нарушение принципа единой ответственности.
  • Нарушение принципа DRY.

Решения

  1. Создайте первоклассный объект, представляющий концепцию, в MAPPER

Контекст

Серьезное программное обеспечение имеет множество проверок строк.

Часто они находятся не в тех местах.

Это приводит к ненадежному и поврежденному программному обеспечению.

Простое решение — создавать только реальные и действительные абстракции.

Пример кода

Неверно

<?

// First Example: Address Validation
class Address { 
  function __construct(string $emailAddress) {
     // String validation on Address class violates SRP
     $this->validateEmail($emailAddress);
     // ...
   }

  private function validateEmail(string $emailAddress) {
    $regex = "/[a-zA-Z0-9_-.+]+@[a-zA-Z0-9-]+.[a-zA-Z]+/";
    // Regex is a sample / It might be wrong
    // Emails and Urls should be first class objects

    if (!preg_match($regex, $emailAddress))
    {
      throw new Exception('Invalid email address ' . emailAddress);
    }    
  }
}

// Second Example: Wordle

class Wordle { 
  function validateWord(string $wordleword) {
    // Wordle word should be a real world entity. Not a subset of strings
  }
 }

Правильно

<?

//First Example: Address Validation
class Address { 
  function __construct(EmailAddress $emailAddress) {
     // Email is always valid / Code is cleaner
     // ...
   }
}

class EmailAddress { 
  // We can reuse this object many times avoiding copy-pasting
  string $address; 
  private function __construct(string $emailAddress) {
    $regex = "/[a-zA-Z0-9_-.+]+@[a-zA-Z0-9-]+.[a-zA-Z]+/";
    // Regex is a sample / It might be wrong
    // Emails and Urls are first class objects

    if (!preg_match($regex, $emailAddress))
    {
      throw new Exception('Invalid email address ' . emailAddress);
    }   
    $this->address = $emailAddress;
  }
}

// Second Example: Wordle

class Wordle { 
  function validateWord(WordleWord $wordleword) {
    // Wordle word is a real world entity. Not a subset of string
  }
 }

class WordleWord { 
  function __construct(string $emailAddress) {
    // Avoid building invalid world words
    // For example length != 5
  }
 }

Обнаружение

  • [x] Полуавтоматический

Мы можем проверить все конструкторы, проверяющие строки, и определить отсутствующие понятия.

Теги

  • Первобытная одержимость

Заключение

Небольшие объекты трудно найти.

Примитивные одержимые всегда жалуются об этом виде косвенных обращений.

Создание этих новых небольших концепций сохраняет верность нашей модели биекции и гарантирует, что наши модели всегда будут работоспособными.

Отношения

Code Smell 41 — Нарушители регулярных выражений

Code Smell 04 — Нарушители строк

Подробнее

Кредиты

Фото Бретта Джордана на Unsplash


<цитата>

Менее 10 % кода связано с предполагаемой целью системы; остальное связано с вводом-выводом, проверкой данных, обслуживанием структуры данных и прочей хозяйственной деятельностью.

Мэри Шоу

Великие цитаты о разработке программного обеспечения


Код Запах 122 — Первобытная одержимость


Объекты доступны для выбора. Даже самые маленькие.

<цитата>

TL;DR: используйте маленькие объекты вместо примитивных.

Проблемы

  • Дублирование кода
  • Отсутствуют мелкие объекты
  • Нарушение принципа
  • Fail Fast.
  • Ошибка биекции
  • Нарушения подмножества: электронные письма – это подмножество строк, действительные возрасты – подмножество вещественных чисел, порты – подмножество целых чисел и т. д.
  • Мы распространяем Logic and Behavior во многих местах.
  • Преждевременная оптимизация.

Решения

  1. Создание небольших объектов
  2. Создание отсутствующих абстракций с помощью MAPPER
  3. Использовать объекты-значения

Контекст

Нам очень лень создавать маленькие объекты.

Нам также лень разделять Что и Как.

Нам очень нравится понимать внутреннее то, как все работает.

Нам нужно начать мыслить в стиле белого ящика и смотреть на протокол и поведение небольших компонентов.

Пример кода

Неверно

// Samples borrowed with permission from
// https://towardsdev.com/why-a-host-is-not-a-string-and-a-port-is-not-an-integer-595c182d817c

var port = 8080;

var in = open("example.org", port);
var uri = urifromPort("example.org", port);
var address = addressFromPort("example.org", port);
var path = pathFromPort("example.org", port);

Правильно

// Samples borrowed with permission from
// https://towardsdev.com/why-a-host-is-not-a-string-and-a-port-is-not-an-integer-595c182d817c

const server = Port.parse(this, "www.kivakit.org:8080");
// Port is a smallobject with responsibilities and protocol

let in = port.open(this);
const uri = port.asUri(this);
const address = port.asInetSocketAddress();
const path = port.path(this, "/index.html");

Обнаружение

  • [x] Руководство

Мы можем автоматизировать проверки конструкторов на наличие упущенных возможностей для небольших объектов.

Теги

  • Первобытная одержимость

Заключение

Нам нужно преобразовать наши строки, числа и массивы в небольшие объекты.

Отношения

Code Smell 121 — проверка строк

Code Smell 04 — Нарушители строк

Подробнее

Кредиты

Фото К. Митч Ходж в Unsplash


<цитата>

Итерация позволяет нам постепенно приближаться к какой-либо цели. Мы можем отказаться от шагов, которые уводят нас дальше, и предпочесть шаги, которые приближают нас. По сути, так работает эволюция. Это также лежит в основе работы современного машинного обучения (ML).

Дэйв Фарли

Великие цитаты о разработке программного обеспечения


Code Smell 123 – Смешанные вопросы "Что" и "Как"


Нам нравится смотреть на внутренние шестерни часов, но нам нужно начать сосредотачиваться на стрелках.

<цитата>

TL;DR: не вмешивайтесь в детали реализации. Будьте декларативны. Необязательно.

Проблемы

Решения

  1. Разделите вопросы "Что" и "Как".

Контекст

В индустрии программного обеспечения очень сложно разделить проблемы.

Функциональное программное обеспечение живет веками.

Программное обеспечение для реализации обеспечивает взаимосвязь, и его сложнее изменить.

Выбор мудрых декларативных имен — ежедневная задача.

Пример кода

Неверно

class Workflow {
    moveToNextTransition() {
        // We couple the business rule with the accidental implementation
        if (this.stepWork.hasPendingTasks) {
            throw new Exception('Preconditions are not met yet..');
        } else {
            this.moveToNextStep();
        }
    }
}

Правильно

class Workflow {
    moveToNextTransition() {
        if (!this.canWeMoveOn()) {
            throw new Exception('Preconditions are not met yet..');
        } else {
            this.moveToNextStep();
        }
    }

    canWeMoveOn() {
        // We hide accidental implementation 'the how'
        // under the 'what'
        return !this.stepWork.hasPendingTasks();
    }
}

Обнаружение

  • [x] Руководство

Это семантический и именующий запах.

Теги

  • Читаемость

Заключение

Нам нужно выбрать хорошие имена и при необходимости добавить косвенные слои.

Конечно, преждевременные оптимизаторы будут бороться с нами, говоря нам, что мы тратим вычислительные ресурсы впустую

ресурсы, и им нужно знать, что мы от них скрываем.

Отношения

Code Smell 92 — Имена изолированных подклассов

Code Smell 05 — Нарушители комментариев

Подробнее

Кредиты

Фото Джоша Редда на Unsplash

Идея этого запаха здесь:

Code Smell 118 — вернуть комментарий пользователя False

и здесь:

Твиттер


<цитата>

Мы постоянно взаимодействуем с чужим кодом, который может не соответствовать нашим высоким стандартам, и имеем дело с входными данными, которые могут быть или не быть действительными. Таким образом, нас учат программировать оборонительно. Мы используем утверждения для обнаружения неверных данных и проверки их согласованности.

Эндрю Хант

Великие цитаты о разработке программного обеспечения


Code Smell 124 – расходящиеся изменения


Вы что-то меняете в классе. Вы меняете что-то несвязанное в том же классе

<цитата>

TL;DR: у классов должна быть только одна обязанность и одна причина для изменения.

Проблемы

  • Соединение
  • Дублирование кода
  • Низкая связность
  • Нарушение принципа единой ответственности

Решения

  1. Извлечь класс

Контекст

Мы создаем классы для выполнения обязанностей.

Если объект делает слишком много, он может измениться в разных направлениях.

Пример кода

Неверно

class Webpage {

  renderHTML() {
    renderDocType();
    renderTitle();
    renderRssHeader();
    renderRssTitle();
    renderRssDescription();
   // ...
  }
  // HTML render can change

  renderRssDescription() {
   // ...
  }

  renderRssTitle() {
   // ...
  }

  renderRssPubDate() {
   // ...
  }
  // RSS Format might change

}

Правильно

class Webpage {

  renderHTML() {
    this.renderDocType();
    this.renderTitle();
    (new RSSFeed()).render();
    this.renderRssTitle();
    this.renderRssDescription();
   // ...
  }
  // HTML render can change
}

class RSSFeed {
  render() {
    this.renderDescription();
    this.renderTitle();
    this.renderPubDate();
    // ...
  }  
  // RSS Format might change
  // Might have unitary tests
  // etc
}

Обнаружение

  • [x] Полуавтоматический

Мы можем автоматически обнаруживать большие классы или отслеживать изменения.

Теги

  • Связь

Заключение

Курсы должны соответствовать принципу единой ответственности и иметь только одну причину для изменения.

Если они развиваются по-разному, они делают слишком много.

Отношения

Запах кода 34 – Слишком много атрибутов

Code Smell 94 — Слишком много импорта

Code Smell 14 – Божественные объекты

Подробнее


<цитата>

Дизайн, в котором не учитываются изменения, рискует серьезно изменить дизайн в будущем.

Эрих Гамма

Великие цитаты о разработке программного обеспечения


Code Smell 125 — Отношения IS-A



В школе мы узнали, что наследование представляет собой отношение "есть-а". Это не так.

<цитата>

TL;DR: подумайте о протоколе и поведении, забудьте о наследовании

Проблемы

  • Плохие модели
  • Неожиданное поведение
  • Переопределение подкласса
  • Нарушение принципа замены Лисков

Решения

  1. Думайте о поведении ведет себя как
  2. Предпочитайте композицию наследованию
  3. Создавать подклассы всегда в соответствии с отношением "ведет себя как-а".

Контекст

отношение IS-A исходит из мира данных.

Мы изучили ERD со структурированным проектированием и моделированием данных.

Теперь нам нужно подумать о поведении.

Поведение важно, данные случайны.

Пример кода

Неверно

// If you made Square derive from Rectangle, 
// then a Square should be usable anywhere you expect a rectangle

#include <iostream>

Rectangle::Rectangle(const unsigned width, const unsigned height):
    m_width(width),
    m_height(height)
{
}

unsigned Rectangle::getWidth() const
{
    return m_width;
}

void Rectangle::setWidth(const unsigned width)
{
  /* Width and Height are independent */
    m_width = width;
}

unsigned Rectangle::getHeight() const
{
    return m_height;
}

void Rectangle::setHeight(const unsigned height)
{
    m_height = height;
}

unsigned Rectangle::area() const
{
  /*Valid for both Rectangles and Squares*/
    return m_height * m_width;
}

Square::Square(const unsigned size)
    : Rectangle(size, size)
{
}

// OK for squares, bad for rectangles
// Protocol is bad, width and height are not relevant on squares
void Square::setWidth(const unsigned size)
{
    m_height = size;
    m_width = size;
}

// OK for squares, bad for rectangles
// Protocol is bad, width and height are not relevant on squares
void Square::setHeight(const unsigned size)
{
    m_height = size;
    m_width = size;
}

void process(Rectangle& r)
{
    unsigned h = 10;
    auto w = r.getWidth();
    r.setHeight(h);

    std::cout << "Expected area: " << (w*h) << ", got " << r.area() << "n";
    // area is not well defined in squares
    // every square IS-A rectangle, but does not behave-like a rectangle
}

int main()
{
    Rectangle rectangle{3,4};
    Square square{5};
    process(rectangle);
    process(square);
}

Правильно

// If you made Square derive from Rectangle, 
// then a Square should be usable anywhere you expect a rectangle

#include <iostream>

Rectangle::Rectangle(const unsigned width, const unsigned height):
    m_width(width),
    m_height(height)
{
}

void Rectangle:changeWidth(const unsigned width)
{
  /* Width and Height are independant */
    m_width = width;
    // We should discuss if it is good to mutate 
    // and not create a new Figure
}

void Rectangle::changeHeight(const unsigned height)
{
    m_height = height;
}

unsigned Rectangle::area() const
{ 
    return m_height * m_width;
}

// No inheritance
Square::Square(const unsigned size):
    m_size(size)
{
}

unsigned Square::area() const
{ 
    return m_size * m_size;
}

void Square::changeSize(const unsigned size)
{
    m_size = size; 
}

void testRectangleChange(Rectangle& r)
{
    unsigned h = 10;
    auto w = r.getWidth();
    r.setHeight(h);

    std::cout << "Expected area: " << (w*h) << ", got " << r.area() << "n";
    // area is not well defined in squares
    // every square IS-A rectangle, but does not behave-like a rectangle
}

int main()
{
    Rectangle rectangle{3,4};
    Square square{5};
    testRectangleChange(rectangle);
    testRectangleChange(square);
}

Обнаружение

  • [x] Руководство

Это семантический запах.

Теги

  • Наследование

Заключение

Вещественное число IS-A Комплексное число (согласно математике).

Целое число IS-A Вещественное число (согласно математике).

Настоящее число не ведет себя как сложное число.

Мы не можем использовать real.setImaginaryPart(), так что это не комплекс в соответствии с нашей биекцией

Отношения

Code Smell 92 — Имена изолированных подклассов

Code Smell 11 — Подклассификация повторного использования кода< /а>

Code Smell 37 – Защищенные атрибуты

Подробнее

Кредиты

Фото Джошуа Рондо на Unsplash


<цитата>

СУХОЙ – Не повторяйтесь – каждая часть знаний должна иметь единое, недвусмысленное и авторитетное представление в системе.

Энди Хант

Великие цитаты о разработке программного обеспечения


И это пока все…

В следующей статье мы расскажем еще о 5 запахах кода!


Оригинал
PREVIOUS ARTICLE
NEXT ARTICLE