Как найти вонючие части вашего кода [Часть XXIV]
20 октября 2022 г.Запахи кода — это классика.
Это пахнет, потому что, вероятно, есть много случаев, когда его можно отредактировать или улучшить.
n Большинство этих запахов являются лишь намеками на то, что что-то может быть не так. Их не обязательно исправлять как таковые… (Тем не менее, вы должны изучить это.)
Пахнет предыдущим кодом
- Часть I< /а>
- Часть II< /а>
- Часть III< /а>
- Часть IV< /а>
- Часть V< /а>
- Часть VI< /а>
- Часть VII< /а>
- Часть VIII< /а>
- Часть IX< /а>
- Часть X< /а>
- Часть XI< /а>
- Часть XII
- Часть XIII
- Часть XIV
- Часть XV
- Часть XVI
- Часть XVII
- Часть XVIII
- Часть XIX
- Часть XX
- Часть XXI
- Часть XXII
- Часть XXIII n
Продолжим...
Code Smell 116 — переменные, объявленные с помощью 'var'
Var, Let, Const: все они одинаковы, не так ли?
<цитата>TL;DR: разумно выбирайте имена переменных, область действия и изменяемость.
Проблемы
- Изменяемость ли>
- Читаемость
Решения
- Объявляйте const все переменные, если вам не нужно их изменять.
Контекст
Большинство языков не нуждаются в объявлениях переменных.
Некоторые другие языки позволяют нам указывать изменчивость.
Мы должны быть строгими и четкими в своих заявлениях.
Пример кода
Неверно
var pi = 3.14
var universeAgeInYears = 13.800.000.000
pi = 3.1415 // no error
universeAgeInYears = 13.800.000.001 // no error
Правильно
const pi = 3.14 //Value cannot mutate or change
let universeAgeInYears = 13.800.000.000 //Value can change
pi = 3.1415 // error. cannot define
universeAgeInYears = 13.800.000.001 // no error
Обнаружение
- [x] Руководство
С помощью тестирования на мутации, задав объявление 'const', мы можем проверить, остается ли значение постоянным, и быть более декларативный, явно применяя его.
Теги
- Изменчивость
- JavaScript
Заключение
Удобочитаемость всегда очень важна.
Нам необходимо явно указать наши намерения и обычаи.
Отношения
Code Smell 86 — Изменяемые массивы констант
Подробнее
<цитата>
Точно так же, как рекомендуется делать все поля закрытыми, если они не нуждаются в большей видимости, также рекомендуется делать все поля окончательными, если только они не должны быть изменяемыми.
Брайан Гетц
Великие цитаты о разработке программного обеспечения
Code Smell 117 — Нереалистичные данные
Программисты ленивы и редко пытаются учиться в реальных областях бизнеса.
<цитата>TL;DR: используйте реальные сценарии и реальные данные (когда это возможно)
Проблемы
- Бибекция Нарушение
- Плохие сценарии использования
- Читаемость
Решения
- Заменить тестовые данные на реальные.
- Используйте MAPPER для сопоставления реальных объектов и реальных данных.
Контекст
В прошлом разработчики подделывали данные домена.
Мы сочли Hello Word хорошей практикой и протестировали ее на абстрактных данных. .
Мы разработали с использованием водопадной модели, очень далекой от реальных пользователей.
С помощью методов биекции и MAPPER, DDD и TDD, приемочное тестирование пользователей стало более важным.
Используя методологии Agile, нам нужно провести тестирование на реальных данных.
Если мы находим ошибку в производственной системе, нам нужно добавить кейс, покрывающий точную ошибку с реальными данными.
Пример кода
Неверно
class BookCartTestCase(unittest.TestCase):
def setUp(self):
self.cart = Cart()
def test_add_book(self):
self.cart.add_item('xxxxx', 3, 10)
#This is not a real example
self.assertEqual(self.cart.total, 30, msg='Book Cart total not correct after adding books')
self.assertEqual(self.cart.items['xxxxx'], 3, msg='Quantity of items not correct after adding book')
def test_remove_item(self):
self.cart.add_item('fgdfhhfhhh', 3, 10)
self.cart.remove_item('fgdfhhfhrhh', 2, 10)
#We made a typo since example is not a real one
self.assertEqual(self.cart.total, 10, msg='Book Cart total not correct after removing book')
self.assertEqual(self.cart.items['fgdfhhfhhh'], 1, msg='Quantity of books not correct after removing book')
Правильно
class BookCartTestCase(unittest.TestCase):
def setUp(self):
self.cart = Cart()
def test_add_book(self):
self.cart.add_item('Harry Potter', 3, 10)
self.assertEqual(self.cart.total, 30, msg='Book Cart total not correct after adding books')
self.assertEqual(self.cart.items['Harry Potter'], 3, msg='Quantity of items not correct after adding book')
#We don't reuse same example.
#We use a new REAL book
def test_remove_item(self):
self.cart.add_item('Divergent', 3, 10)
self.cart.remove_item('Divergent', 2, 10)
self.assertEqual(self.cart.total, 10, msg='Book Cart total not correct after removing book')
self.assertEqual(self.cart.items['Divergent'], 1, msg='Quantity of books not correct after removing book')
Обнаружение
- [x] Руководство
Это семантический запах.
Исключения
В некоторых доменах и в соответствии с законодательством мы не можем использовать реальные данные.
Мы должны подделать его с помощью значимых данных.
Теги
- Тестирование
Заключение
Комментарии к коду — это запах кода.
Чтение тестов — единственный способ узнать, как работает программа.
Нам нужно быть более точными в наших тестах.
Отношения
Code Smell 05 — Нарушители комментариев
Подробнее
Кредиты
Фото Натальи Хофманн на Unsplash
Спасибо Кертису Эйнсманну
<цитата>
Вы действительно ничего не понимаете, если не можете объяснить это своей бабушке.
Альберт Эйнштейн
Великие цитаты о разработке программного обеспечения
Код Запах 118 — Возврат False
Неудобно проверять, возвращает ли логическое условие логическое значение
<цитата>TL;DR: не возвращайте явные логические значения. Большинство логических значений — это запахи кода.
Проблемы
- Декларативность
- Код ниндзя
- Внедрение решений
Решения
- Вернуть логическое предложение вместо проверки отрицания.
- Ответ должен быть формулой бизнес-логики, а не алгоритмом.
Контекст
При работе с булевыми формулами удобнее показать бизнес-логическую формулу, чем вводить инвертированный IF.
Программисты склонны возвращать случайные имплементационные решения вместо реальных бизнес-правил.
Пример кода
Неверно
function canWeMoveOn() {
if (work.hasPendingTasks())
return false;
else
return true;
}
Правильно
function canWeMoveOn() {
return !work.hasPendingTasks();
}
Обнаружение
- [x] Автоматически
На основе синтаксических деревьев мы можем безопасно реорганизовать код.
Теги
- логическое значение
Заключение
Остерегайтесь возврата логических значений.
После возврата вам понадобится оператор If, который также является запахом кода. р>
Отношения
Code Smell 115 – вернуть True< /p>
Code Smell 101 — Сравнение с логическими значениями
Code Smell 24 — Логические приведения
Code Smell 62 — Переменные флага< /p>
Code Smell 102 – код стрелки< /p>
Code Smell 51 — Двойное отрицание
Подробнее
Кредиты
Фото: Морган Хаузел на Unsplash
Спасибо Нико К. за это предложение.
<цитата>
Совсем не важно, чтобы все получилось правильно с первого раза. Крайне важно сделать все правильно в последний раз.
Эндрю Хант
Великие цитаты о разработке программного обеспечения
Код Запах 119 — Лестничный код
Вложенные логические условия выражают бизнес-правило. Не ЕСЛИ
<цитата>TL;DR: Избегайте проверки логических выражений и возврата явного логического значения.
Проблемы
- Декларативность
- Код ниндзя
- Читаемость
- Код стрелки
Решения
- Возвращает логическое значение бизнес-формулы.
Контекст
При работе с логическими формулами более читабельно показывать бизнес-логическую формулу, чем ступенчатые логические проверки, за которыми следует возврат явного значения true/false;
Пример кода
Неверно
def is_platypus(self):
if self.is_mammal():
if self.has_fur():
if self.has_beak():
if self.has_tail():
if self.can_swim():
return True
return False
# This is also wrong since it is polluted with IFs and not readable by a biologist
def is_platypus(self):
if not self.is_mammal():
return False
if not self.has_fur():
return False
if not self.has_beak():
return False
if not self.has_tail():
return False
if not self.can_swim():
return False
return True
Правильно
def is_platypus(self):
return self.is_mammal() && self.has_fur() && self.has_beak() && self.has_tail() && self.can_swim()
# We can even group conditions according to animal taxonomies
Обнаружение
- [x] Автоматически
На основе синтаксических деревьев мы можем безопасно реорганизовать код, удалив явное логическое значение.
Теги
- логическое значение
Заключение
Остерегайтесь возврата логических значений.
После возврата вам понадобится оператор If, который также является запахом кода. р>
Отношения
Code Smell 115 – вернуть True< /p>
Code Smell 101 — Сравнение с логическими значениями
Code Smell 24 — Логические приведения
Code Smell 62 — Переменные флага< /p>
Code Smell 102 – код стрелки< /p>
Code Smell 80 — вложенная попытка/поймать
Подробнее
Кредиты
Фото Юкана Татеиси на Unsplash
Еще раз спасибо Нико К. за это предложение.
<цитата>
Настоящий герой программирования — это тот, кто пишет отрицательный код.
Дуглас Макилрой
Великие цитаты о разработке программного обеспечения
Code Smell 120 – последовательные идентификаторы
Большинство IDS — это запахи кода. Последовательные идентификаторы также являются уязвимостью
<цитата>TL;DR: не выставляйте очевидные последовательные идентификаторы.
Проблемы
Решения
- Используйте неочевидные ключи.
- Используйте темные клавиши или UUID.
Контекст
Идентификаторы представляют собой проблему при работе с объектами домена.
Идентификаторы не существуют в реальном мире, поэтому они нарушают нашу биекцию.
Идентификаторы следует использовать только при раскрытии внутренних ресурсов внешнему миру за пределами системы.
Это всегда случайные проблемы, и они не должны мешать нашей модели.
Пример кода
Неверно
class Book {
private Long bookId; // book knows its ID
private List<Long> authorIds; // book knows author IDs
}
Book harryPotter = new Book(1, {2});
Book cleanCode = new Book(2, {4});
Book donQuixote = new Book(3, {5});
// We can scrape from now on.
Правильно
class Author {
// .. Author protocol
}
class Book {
private List<Author> authors; // book knows authors
// No strange behavior. just what a book can do
// Real books don't know about IDs
// ISBN is accidental to a book. Readers don't care
}
class BookResource {
private Book resource; // The resource knows the underlying book
private id; // The id is the link we provide to external world
}
Book harryPotter = new Book(new Author('J. K. Rowling'));
Book cleanCode = new Book(new Author('Robert Martin'))
Book donQuixote = new Book(new Author('Miguel Cervantes'));
BookResource harryPotterResource = new BookResource(harryPotter, UUID.randomUUID());
// Books don't know they id. Just the resource does
Обнаружение
- [x] Автоматически
Мы можем использовать методы пентестинга в нашей системе, чтобы обнаружить этот запах.
Теги
- Безопасность
Заключение
Если нам нужно показать внутренние объекты внешнему миру, мы должны использовать неочевидные идентификаторы.
Таким образом, мы можем обнаруживать (и блокировать) атаки грубой силы, отслеживая трафик и ошибки 404.. р>
Подробнее
Кредиты
Фото Макс Бендер на Unsplash
Спасибо, @davidkroell, за совет KSUID.
<цитата>
Единственная по-настоящему безопасная система — это та, которая выключена, залита бетонным блоком и заперта в помещении со свинцовой облицовкой под вооруженной охраной.
Джин Спаффорд
Великие цитаты о разработке программного обеспечения
И это пока все…
В следующей статье мы расскажем еще о 5 запахах кода!
Оригинал