Как найти вонючие части вашего кода [Часть XXV]
1 ноября 2022 г.Изображение cookie_studio на Freepik
Запахи кода — это классика.
Это пахнет, потому что, вероятно, есть много случаев, когда его можно отредактировать или улучшить.
n Большинство из этих запахов — всего лишь намеки на то, что что-то может быть не так. Они не обязаны исправляться как таковые… (Тем не менее, вам следует изучить это.)
Пахнет предыдущим кодом
- Часть I< /а>
- Часть II< /а>
- Часть III< /а>
- Часть IV< /а>
- Часть V< /а>
- Часть VI< /а>
- Часть VII< /а>
- Часть VIII< /а>
- Часть IX< /а>
- Часть X< /а>
- Часть XI< /а>
- Часть XII
- Часть XIII
- Часть XIV
- Часть XV
- Часть XVI
- Часть XVII
- Часть XVIII
- Часть XIX
- Часть XX
- Часть XXI
- Часть XXII
- Часть XXIII
- Часть XXIV n
Продолжим...
Code Smell 121 — Проверка строк
Вам необходимо проверить строки. Таким образом, вам вообще не нужны строки
<цитата>TL;DR: поиск отсутствующих объектов домена при проверке строк.
Проблемы
- Примитивная одержимость.
- Ошибка биекции
- Проверенные строки — это подмножество всех возможных строк. Нарушение принципа
- Fail Fast.
- Нарушение принципа единой ответственности.
- Нарушение принципа DRY.
Решения
- Создайте первоклассный объект, представляющий концепцию, в 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 — Нарушители строк
Подробнее
- Единственный и неповторимый принцип проектирования программного обеспечения
- Как разработать игру Wordle< /li>
- реификация объектов
Кредиты
Фото Бретта Джордана на Unsplash
<цитата>
Менее 10 % кода связано с предполагаемой целью системы; остальное связано с вводом-выводом, проверкой данных, обслуживанием структуры данных и прочей хозяйственной деятельностью.
Мэри Шоу
Великие цитаты о разработке программного обеспечения
Код Запах 122 — Первобытная одержимость
Объекты доступны для выбора. Даже самые маленькие.
<цитата>TL;DR: используйте маленькие объекты вместо примитивных.
Проблемы
- Дублирование кода
- Отсутствуют мелкие объекты Нарушение принципа
- Fail Fast.
- Ошибка биекции
- Нарушения подмножества: электронные письма – это подмножество строк, действительные возрасты – подмножество вещественных чисел, порты – подмножество целых чисел и т. д.
- Мы распространяем Logic and Behavior во многих местах.
- Преждевременная оптимизация.
Решения
- Создание небольших объектов
- Создание отсутствующих абстракций с помощью MAPPER
- Использовать объекты-значения
Контекст
Нам очень лень создавать маленькие объекты.
Нам также лень разделять Что и Как.
Нам очень нравится понимать внутреннее то, как все работает.
Нам нужно начать мыслить в стиле белого ящика и смотреть на протокол и поведение небольших компонентов.
Пример кода
Неверно
// 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: не вмешивайтесь в детали реализации. Будьте декларативны. Необязательно.
Проблемы
- Случайное соединение
- Соединение
- Отсутствие дизайна для изменений
- Комментарии различают "как" и "что".
Решения
- Разделите вопросы "Что" и "Как".
Контекст
В индустрии программного обеспечения очень сложно разделить проблемы.
Функциональное программное обеспечение живет веками.
Программное обеспечение для реализации обеспечивает взаимосвязь, и его сложнее изменить.
Выбор мудрых декларативных имен — ежедневная задача.
Пример кода
Неверно
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: у классов должна быть только одна обязанность и одна причина для изменения.
Проблемы
- Соединение
- Дублирование кода
- Низкая связность
- Нарушение принципа единой ответственности
Решения
- Извлечь класс
Контекст
Мы создаем классы для выполнения обязанностей.
Если объект делает слишком много, он может измениться в разных направлениях.
Пример кода
Неверно
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: подумайте о протоколе и поведении, забудьте о наследовании
Проблемы
- Плохие модели
- Неожиданное поведение
- Переопределение подкласса
- Нарушение принципа замены Лисков
Решения
- Думайте о поведении ведет себя как
- Предпочитайте композицию наследованию
- Создавать подклассы всегда в соответствии с отношением "ведет себя как-а".
Контекст
отношение 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 запахах кода!
Оригинал