KISS, DRY, YAGNI, SOLID, LSP, POLA и друзья

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

Такие принципы, как KISS, YAGNI, DRY и SOLID, теперь, к счастью, известны многим разработчикам и даже преподаются во многих курсы обучения.

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

Но почему эти принципы так важны?

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

Мы все согласны с тем, что плохой код это:

  • Трудно понять
  • Более сложный, чем должен быть
  • Нелегко протестировать
  • Разочаровывает

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

Проще всего определить код как хороший, если он соответствует следующим принципам:

  • Избегайте дублирования (DRY)
  • Сохраняйте код простым (KISS)
  • Избегайте ненужных функций (YAGNI)
  • Скрыть детали реализации (Абстракция)
  • Сохраняйте расширяемость кода (Расширяемость)
  • Сделайте свое программное обеспечение модульным (SoC)
  • Предпочитайте композицию наследованию
  • Предпочитайте удобочитаемость краткости.
  • Добейтесь максимальной сплоченности
  • Дайте классу только одну обязанность (SRP)
  • Заставьте объекты следовать принципу открытия-закрытия.
  • Дизайн по контракту (LSP)
  • Не заставляйте клиентов зависить от интерфейсов, которые они не используют (ISP).
  • Зависит от абстракций, а не от конкреций (DIP)
  • Сделайте свою программу POLA (принцип наименьшего удивления)

Но их слишком много! Как мы можем уважать их всех? Первым шагом является хорошее понимание того, какую проблему решают эти принципы, а затем как их реализовать.

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

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

СУХОЙ: не повторяйся

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

Сценарий

Ваш код содержит дублирование знаний: не только дублирование кода, но и одну и ту же логику, выраженную по-разному в разных местах.

Причина

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

Связанные методы

Абстракции(следуя общепринятым принципам языков ООП), автоматизация(говоря о дублировании процессов) и нормализация(когда у нас много представлений данных, например, в большой базе данных).

KISS: Будь проще, глупец

В концепции KISS мы не хотим ни меньше, ни больше, мы просто хотим иметь ровно столько, сколько требуется.

Сценарий

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

Причина

KISS — это скорее философия, чем принцип: он побуждает нас поддерживать чистоту нашего кода с конечной целью сделать его более эффективным и обслуживаемый.

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

Связанные методы

Мы можем внедрить KISS, соблюдая принципы SOLID (в частности, SRP), постоянно рефакторинг кода

ЯГНИ: Вам это не понадобится

Всегда внедряйте вещи, когда они вам действительно нужны, а не когда вы просто предвидите, что они вам нужны.

Сценарий

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

Причина

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

Связанные методы

YAGNI — это одна из мантр экстремального программирования, поэтому она используется в основном в средах Agile.

Примечание: YAGNI связан с KISS, но это не одно и то же: YAGNI говорит вам, что вы должны выбрать функции, которые вам действительно нужны, KISS говорит вам, что вы должны представлять их простым и эффективным способом.

Абстракция: скрыть детали реализации.

Абстракция скрывает детали реализации, предоставляя слой поверх базовой функциональности.

Сценарий

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

Причина

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

Связанные методы

Концепция абстракции используется, в частности, для языков ООП (таких как абстрактные классы Java). Существует множество шаблонов проектирования, полезных для реализации этой концепции, например Factory, Adapter, Bridge и Template шаблоны.

Расширяемость:сохраняйте расширяемость кода.

Расширяемость — это способность кода легко расширяться и изменяться в соответствии с новыми требованиями или функциями.

Сценарий

Вы написали хорошее программное обеспечение с хорошей закрытой архитектурой.
Однажды ваш начальник говорит вам: «Требования изменены», но, к сожалению, ваша хорошая архитектура не готова к адаптации, поэтому вам нужно начинать почти с нуля.

Причина

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

Связанные методы

Расширяемость связана с концепцией модульности (см. SoC): модульныйкод легче расширять и изменять, чем монолитныйкод. Но также с абстракцией, принципом открытости/закрытости и инверсией зависимостей. Хорошим шаблоном проектирования для реализации расширяемости является шаблон Стратегия.

SoC: разделение задач

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

Сценарий

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

Причина

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

Связанные методы

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

SRP: принцип единой ответственности

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

Сценарий

Хорошо, вы разделили свое программное обеспечение на большее количество модулей. Отлично.
Но каждый модуль (и класс) по-прежнему делает слишком много вещей, и, вероятно, они могут изменить или повлиять на поведение других модулей.

Причина

Этот принцип часто сводится к следующему: «делай что-то одно и делай это хорошо».
Класс или модуль с несколькими обязанностями со временем может стать трудным для понимания и сопровождения. Ограничив каждый класс или модуль одной обязанностью, вы можете гарантировать, что код останется целенаправленным и с ним будет легко работать.

Связанные методы

На самом деле связанный с SoC, этот принцип может быть реализован также с помощью шаблонов Decorator и Facade. Это также связано с концепцией сплоченности и принципом предпочтения композиции наследованию.

OCP: принцип открытого-закрытого

Программные объекты, такие как классы, модули и функции, должны быть открыты для расширения, но закрыты для модификации.

Сценарий

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

Причина

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

Связанные методы

OCP часто используется в сочетании с другими принципами проектирования, такими как принцип инверсии зависимостей (DIP) и самим SRP. Однако его проще реализовать во время проектирования архитектуры, используя некоторые шаблоны проектирования, такие как Стратегия, Фабрика и Абстрактная фабрика.

Предпочитайте композицию наследованию

Классы должны обеспечивать полиморфное поведение и повторно использовать классы с помощью композиции.

Сценарий

Ваш код злоупотребляет наследованием. Классы образуют лабиринт иерархий и зависимостей и часто имеют отношения «имеет-а» вместо «есть-а».

Причина

Наследование — это здорово, оно помогает нам решить многие проблемы, о которых мы говорим в этой статье. Однако все переменные, а также методы базового класса наследуются, даже если он не используется. Это приводит к ненужному горлышку бутылки. Когда у нас есть отношение "имеет", лучше использовать композицию, потому что зависимостей меньше, и это позволяет нам определить более естественный Генеалогическое древо между компонентами вашего кода.
Затем композиция обеспечивает лучшую тестируемость класса, чем наследование.

Связанные методы

Это один из ключевых принципов языков ООП. Опять же, очень полезным шаблоном проектирования является Стратегия, в котором композиция и делегирование используются для изменения поведения «Контекста», не касаясь его кода.
Многие библиотеки наборов тестов предлагают использовать композицию, так как проще определить фиктивные объекты при создании модульных тестов.

Максимальная сплоченность

Стремитесь к низкой связанности и высокой связности

Сценарий

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

Причина

Низкая связанность между модулями снижает влияние изменений в одном модуле на другие модули. Высокая связанность – это степень, в которой элементы внутри модуля связаны друг с другом. Модуль с высокой связностью будет иметь элементы, которые связаны между собой и работают вместе для выполнения одной четко определенной задачи. Это упрощает понимание, модификацию и обслуживание модуля.

Связанные методы

Эта концепция является одним из ключей к соблюдению других принципов, таких как Инкапсуляция, Скрытие информации и Разделение ответственности. Его также можно реализовать с использованием таких шаблонов проектирования, как Observer, Strategy и Factory.

ISP: принцип разделения интерфейсов

Клиенты не должны зависеть от интерфейсов, которые они не используют

Сценарий

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

Причина

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

Связанные методы

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

LSP: принцип замещения Лисков

Если S является подтипом T, то объекты типа T могут быть заменены объектами типа S без изменения каких-либо желаемых свойств программы (корректность, выполняемая задача и т. д.).

Сценарий

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

Причина

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

Связанные методы

Этот принцип связан с методологией проектирования Design by Contract, в которой особое внимание уделяется указанию поведения и обязательств программных компонентов с использованием предварительных условий, постусловий и инварианты.

DIP: принцип инверсии зависимостей

Сущности (модули, классы и т. д.) должны зависеть от абстракций, а не от конкретики.

Сценарий

В вашем коде есть классы, напрямую зависящие от реализации других классов. Например, в методе calculatePrice() вы создаете экземпляр класса Consoleдля печати расчетов. Трудно тестировать метод calculatePrice() изолированно, так как мы не можем легко имитировать Console class.

Причина

Мы уже видели важность разъединения. Целью DIP является отделение высокоуровневых модулей от низкоуровневых, чтобы изменения в одном модуле не влияли на другой. Модуль высокого уровня должен определять интерфейс или абстрактный класс, который модуль низкого уровня реализует или от которого наследует. Это позволяет заменять различные реализации низкоуровневого модуля, не влияя на поведение высокоуровневого модуля.

Связанные методы

DIP часто реализуется с использованием шаблона Dependency Injection, который включает в себя предоставление объекта с его зависимостями из внешнего источника, вместо того, чтобы объект сам создавал свои зависимости и управлял ими. Также существует несколько фреймворков и библиотек, поддерживающих DI, например Spring для Java, Angular для JavaScript и .NET Core для C#.

ПОЛА: принцип наименьшего удивления

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

Сценарий

Ваше программное обеспечение имеет графический интерфейс, который не соответствует общепринятым шаблонам и соглашениям интерфейса. Кроме того, некоторые внутренние API, которые вы создали, не следуют соглашениям программирования. Любой, кто читает этот код, не понимает, есть ли причина, по которой код такой, какой он есть, или нет.

Причина

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

Связанные методы

POLA связан с концепцией согласованности, практикой использования одних и тех же шаблонов проектирования и соглашений во всей системе для создания предсказуемого и интуитивно понятного взаимодействия с пользователем. Это также связано с идеей юзабилити-тестирования — процесса оценки пользовательского интерфейса или дизайна системы путем наблюдения за тем, как пользователи взаимодействуют с ней.

Предпочитайте читабельность краткости

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

Сценарий

Ваш код полон переменных с именами a, b, c, x, y, var1, arr,и т. д.
Любой, кто Читатели этого кода (даже вы!) через неделю не смогут понять смысл того, что они читают.

Причина

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

Связанные методы

Этот принцип часто вспоминают, когда мы говорим о рефакторинге. Этот принцип также важен при написании документации(также внутри исходного кода). Кроме того, это помогает реализовать модульные тесты.

Выводы

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

Однако я думаю, что не важно, пока мы разрабатываем, думать типа «Здесь я должен сделать это, потому что я должен соблюдать принцип единой ответственности». Важно понять и убедить себя, что эти принципы важны. Спросите звездного шеф-повара, в чем секрет звездного ресторана, и он обязательно ответит, что это чистота и упорядоченность компонентов кухни.

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

Повышение уровня кодирования

Спасибо, что являетесь частью нашего сообщества! Перед тем, как ты уйдешь:

  • 👏 Хлопайте за историю и подписывайтесь на автора 👉
  • 📰 Смотрите больше контента в публикации Level Up Coding
  • 💰 Бесплатный курс собеседования по программированию ⇒ Просмотреть курс
  • 🔔 Подписывайтесь на нас: Twitter | ЛинкедИн | "Новостная рассылка"

🚀👉 Присоединяйтесь к коллективу талантов Level Up и найдите прекрасную работу