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

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

6 декабря 2022 г.

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

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

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

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

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


Code Smell 131 — конструктор без аргументов

Объекты, созданные без аргументов, часто изменяемы и ошибочны

<цитата>

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

Проблемы

Решения

  1. Используйте один полный и единственный конструктор.
  2. Избегайте сеттеров и геттеров

Контекст

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

Beans — хорошо известный пример запаха кода.

Пример кода

Неверно

 public Person();

// Anemic and mutable

Правильно

public Person(String name, int age) {
     this.name = name;
     this.age = age;
     } 
 }

// We 'pass' the essence to the object 
// So it does not mutate

Обнаружение

  • [x] Автоматически

Мы можем проверить все конструкторы, но есть несколько ложных срабатываний.

Объекты без сохранения состояния являются допустимым примером.

Теги

  • Изменчивость

Заключение

Пустые конструкторы — это намеки на изменчивость и случайные проблемы с реализацией.

Нам необходимо изучить способы использования, чтобы улучшить наши решения.

Отношения

Code Smell 68 — Добытчики

Code Smell 28 — Сеттеры

Code Smell 01 — Анемичные модели

Code Smell 40 — DTO

Подробнее

Кредиты

Фото Аде Адебовале на Unsplash


<цитата>

Не беспокойтесь о дизайне, если вы прислушаетесь к своему коду, появится хороший дизайн... Прислушивайтесь к техническим специалистам. Если они жалуются на трудности внесения изменений, отнеситесь к таким жалобам серьезно и дайте им время исправить ситуацию.

Мартин Фаулер

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


Code Smell 132 – Исключение: попытка слишком широкая

Исключения удобны. Но должен быть как можно более узким

<цитата>

TL;DR: Будьте максимально конкретны при обработке ошибок.

Проблемы

  • Нарушение принципа Fail fast
  • Отсутствующие ошибки
  • Ложноотрицательные результаты

Решения

  1. Максимально сузьте обработчик исключений

Пример кода

Неверно

import calendar, datetime
try: 
    birthYear= input('Birth year:')
    birthMonth= input('Birth month:')
    birthDay= input('Birth day:')
    # we don't expect the above to fail
    print(datetime.date(int(birthYear), int(birthMonth), int(birthDay)))
except ValueError as e:
    if str(e) == 'month must be in 1..12': 
        print('Month ' + str(birthMonth) + ' is out of range. The month must be a number in 1...12')
    elif str(e) == 'year {0} is out of range'.format(birthYear): 
        print('Year ' + str(birthYear) + ' is out of range. The year must be a number in ' + str(datetime.MINYEAR) + '...' + str(datetime.MAXYEAR))
    elif str(e) == 'day is out of range for month': 
        print('Day ' + str(birthDay) + ' is out of range. The day must be a number in 1...' + str(calendar.monthrange(birthYear, birthMonth)))

Правильно

import calendar, datetime

# We might add specialized tries dealing with errors from the following 3 statements
birthYear= input('Birth year:')
birthMonth= input('Birth month:')
birthDay= input('Birth day:')
# try scope should be narrow
try: 
    print(datetime.date(int(birthYear), int(birthMonth), int(birthDay)))
except ValueError as e:
    if str(e) == 'month must be in 1..12': 
        print('Month ' + str(birthMonth) + ' is out of range. The month must be a number in 1...12')
    elif str(e) == 'year {0} is out of range'.format(birthYear): 
        print('Year ' + str(birthYear) + ' is out of range. The year must be a number in ' + str(datetime.MINYEAR) + '...' + str(datetime.MAXYEAR))
    elif str(e) == 'day is out of range for month': 
        print('Day ' + str(birthDay) + ' is out of range. The day must be a number in 1...' + str(calendar.monthrange(birthYear, birthMonth)))

Обнаружение

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

Если у нас есть достаточно хороший набор тестов, мы можем выполнить мутационное тестирование, чтобы максимально сузить область исключений.

Теги

  • Исключения

Заключение

Мы должны делать исключения как можно хирургичнее.

Отношения

Code Smell 26 — Исключения, загрязняющие окружающую среду

Code Smell 73 — Исключения для ожидаемых случаев

Кредиты

Фотон от Якоба Брауна на Unsplash


<цитата>

Основная обязанность обработчика исключений — передать ошибку из-под опеки программиста и передать удивленному лицу пользователя.

Верити Стоб

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


Code Smell 133 — Жестко запрограммированные условия IF

Жесткое кодирование — это нормально. Ненадолго

<цитата>

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

Проблемы

  • Тестируемость
  • Жестко закодированные значения
  • Нарушение принципа открытости/закрытости

Решения

  1. Замените все IF динамическим условием или полиморфизм.

Контекст

Жесткое кодирование условий iF отлично подходит для разработки через тестирование.

Нам нужно убрать вещи.

Пример кода

Неверно

private string FindCountryName (string internetCode)
{
  if (internetCode == "de")
    return "Germany";
  else if(internetCode == "fr") 
    return "France";
  else if(internetCode == "ar")
    return "Argentina";
    // lots of elses
  else
    return "Suffix not Valid";
}

Правильно

private string[] country_names = {"Germany", "France", "Argentina"} // lots more
private string[] Internet_code_suffixes= {"de", "fr", "ar" } // more

private Dictionary<string, string> Internet_codes = new Dictionary<string, string>();

// There are more efficient ways for collection iteration
// This pseudocode is for illustration
int currentIndex = 0; 
foreach (var suffix in Internet_code_suffixes) {
  Internet_codes.Add(suffix, Internet_codes[currentIndex]);
  currentIndex++;
}

private string FindCountryName(string internetCode) {
  return Internet_codes[internetCode];
}

Обнаружение

  • [x] Автоматически

Проверив условия If/else, мы можем обнаружить жестко закодированные условия.

Теги

  • IF

Заключение

Раньше жесткое кодирование было невозможно.

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

Отношения

Code Smell 36 — Switch/case/elseif Операторы /else/if

Code Smell 102 – код стрелки< /p>

Подробнее

Кредиты

Фото Джессики Джонстон на Unsplash


<цитата>

Не будь (слишком) умным. Моя цель состояла в том, чтобы не поощрять слишком умный код, потому что «умный код» трудно написать, легко ошибиться, сложнее поддерживать и часто не быстрее, чем более простые альтернативы, потому что его трудно оптимизировать.

Бьерн Страуструп

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


Code Smell 134 – Специализированные бизнес-коллекции

Если он ходит как утка и крякает как утка, значит, это утка

<цитата>

TL;DR: не создавайте ненужных абстракций

Проблемы

  • Над дизайном
  • Ненужные классы

Решения

  1. Используйте стандартный класс

Контекст

Обнаружение абстракций в MAPPER – сложная задача.

После уточнения мы должны удалить ненужные абстракции.

Пример кода

Неверно

<?php

Namespace Spelling;

final class Dictionary {

    private $words;
    function __construct(array $words) {
        $this->words = $words;
    }

    function wordsCount(): int {
        return count($this->words);
    }

    function includesWord(string $subjectToSearch): bool {
        return in_array($subjectToSearch, $this->words);
    }
}

// This has protocol similar to an abstract datatype dictionary
// And the tests

use PHPUnitFrameworkTestCase;

final class DictionaryTest extends TestCase {
    public function test01EmptyDictionaryHasNoWords() {
        $dictionary = new Dictionary([]);
        $this->assertEquals(0, $dictionary->wordsCount());
    }

    public function test02SingleDictionaryReturns1AsCount() {        
        $dictionary = new Dictionary(['happy']);
        $this->assertEquals(1, $dictionary->wordsCount());
    }

    public function test03DictionaryDoesNotIncludeWord() {
        $dictionary = new Dictionary(['happy']);
        $this->assertFalse($dictionary->includesWord('sadly'));
    }

    public function test04DictionaryIncludesWord() {
        $dictionary = new Dictionary(['happy']);
        $this->assertTrue($dictionary->includesWord('happy'));
    }
} 

Правильно

<?php

Namespace Spelling;

// final class Dictionary is no longer needed

// The tests use a standard class
// In PHP we use associative arrays
// Java and other languages have HashTables, Dictionaries etc. etc.

use PHPUnitFrameworkTestCase;

final class DictionaryTest extends TestCase {
    public function test01EmptyDictionaryHasNoWords() {
        $dictionary = [];
        $this->assertEquals(0, count($dictionary));
    }

    public function test02SingleDictionaryReturns1AsCount() {
        $dictionary = ['happy']; 
        $this->assertEquals(1, count($dictionary));
    }

    public function test03DictionaryDoesNotIncludeWord() {
        $dictionary = ['happy']; 
        $this->assertFalse(in_array('sadly', $dictionary));
    }

    public function test04DictionaryIncludesWord() {
        $dictionary = ['happy'];  
        $this->assertTrue(in_array('happy', $dictionary));
    }
} 

Обнаружение

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

Исходя из протоколов, мы должны удалить некоторые ненужные классы

Исключения

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

Теги

  • Протоколы

Заключение

Время от времени нам нужно очищать код.

Хорошей отправной точкой являются специализированные коллекции.

Отношения

Code Smell 111 – изменение коллекций при обходе

Подробнее

Кредиты

Фото Писита Хенга на Unsplash


<цитата>

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

Витсе Венема

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


Code Smell 135 — Интерфейсы всего с одной реализацией

Хорошо быть универсальным и предвидеть будущее.

<цитата>

TL;DR: не обобщайте слишком много

Проблемы

  • Предполагаемый дизайн
  • Сложность
  • Излишняя инженерия

Решения

  1. Удаляйте интерфейс, пока не получите больше примеров.

Контекст

В прошлом программисты просили нас проектировать с учетом изменений.

Сегодня мы следуем научному методу.

Всякий раз, когда мы находим дублирование, мы удаляем его.

Не раньше.

Пример кода

Неверно

public interface Vehicle {
    public void start();
    public void stop();
}

public class Car implements Vehicle {
    public void start() {
        System.out.println("Running...");
    }
    public void stop() {
        System.out.println("Stopping...");
    }
}

// No more vehicles??

Правильно

public class Car {
    public void start() {
        System.out.println("Running...");
    }
    public void stop() {
        System.out.println("Stopping...");
    }
}

// Wait until more vehicles are discovered

Обнаружение

  • [x] Автоматически

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

Исключения

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

Некоторые платформы определяют интерфейс как протокол, который необходимо выполнить.

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

Интерфейсы — это MAPPER соответствие протоколу.

Протоколы внедрения зависимостей/внедрения объявляют интерфейсы, которые выполняются с их реализациями. До тех пор они могут быть пустыми.

Если ваш язык определяет интерфейс для имитации тестов, это еще один запах кода.

Теги

  • Над дизайном

Отношения

Code Smell 130 — AddressImpl

Code Smell 30 — Насмешливый бизнес

Code Smell 136 — классы только с одним подклассом

Заключение

Нужно ждать абстракций, а не заниматься творчеством и спекуляциями

Кредиты

Фото Brian Kostiuk на Unsplash р>


<цитата>

Я люблю программное обеспечение, потому что если вы можете что-то представить, вы можете это создать.

Рэй Оззи

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


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

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


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