Принципы SOLID — без этого, даже не идите на собеседование!

Если вы не знаете, что такое SOLID, то можете даже не идти на собеседование, да, да, я серьезно ;). Поэтому давайте разбираться: SOLID — это набор принципов, следуя которым, программный код будет более чистым и гибким. Т.е. это не какае-то библиотека или технология, это просто правила, которым должен следовать любой адекватные разработчик, не зависимо на чем он программирует.
- S — Single-responsibility principle — принцип единой ответственности
- O — Open-closed principle — принцип открытости/закрытости.
- L — Liskov substitution principle — принцип подстановки Барбары Лисков
- I — Interface segregation principle — принцип разделения интерфейса
- D — Dependency Inversion Principle — принцип инверсии зависимостей
Запомнить названия по первости будет сложно, да к этому и не нужно стремиться. Главное понимать содержимое и те идеи, которые предлагаются в каждом из них. Я покажу принципы SOLID на примере языка Java, однако смысл применим к любому языку программирования.
S — Single-responsibility principle
Принцип единой ответственности означает, что один класс или файл должен иметь только одну цель и одно единственное назначение. Вы не имеете права создавать классы и файлы, которые представляют собой «комбайн» умеющий делать все.
Например, если ваш класс создан, что-бы отображать данные на экране, то не нужно размещать в этом классе логику получения этих данных из интернета.
Дело в том, что может получится ситуация, что меняя логику загрузки данных из интернета, вы случайно испортите логику отображения данных на экране. Поэтому работу с интернетом и отображением необходимо разделять на два разных класса.
Также, не забываем, что у вас есть интерфейсы и абстрактные классы, с помощью которых вы можете передавать интерфейс логики с интернетом, в класс для отображения. В итоге реализация интерфейса будет конкретная для конкретного случая, т.е. вам достаточно поменять реализацию интерфейса или абстрактного класса, если логика загрузки данных изменится.
Что-бы проверить, соответствует ли ваш класс этому принципу, задайте себе вопрос: Что может случится, из-за чего мне потребуется изменить данный класс?. Если ответов несколько, значит необходимо разделить класс на несколько.
Советую обратить внимание на следующие приемы, которые помогают соблюдать данный принцип:
- Разработка через тестирование (TDD)
- Паттерн «Выделение класса»(Extract Class)
- Паттерн «Фасад»(Facade)
- Паттерн Паттерн «Прокси»(Proxy)
- Паттерн «DAO»
O — Open-closed principle
Принцип открытости/закрытости означает, что программные сущности(классы, интерфейсы и т. д.) должны быть открыты для расширения, но закрыты для модификации.
Например, у вас есть класс, который выполняет определенные функции. Если вам понадобилось добавить дополнительный функционал, то необходимо создать наследника этого класса или использовать композицию. Но, изменять исходный класс запрещено. Это необходимо, что-бы не испортить код, использующий этот класс.
В ряде случаев рекомендуется избегать наследования и применять композицию, что-бы избежать сложных структур данных и сделать код еще более независимым.
Одним словом, изменять код базового класса строго настрого запрещено!
L — Liskov substitution principle
Принцип подстановки Барбары Лисков, самый не понятный принцип из-за названия :). Но все достаточно просто и немного похоже на предыдущий принцип. Принцип гласит, что поведение методов в дочернем классе должно следовать принципам базового класса, а не изменять их. То есть, дочерний класс переопределяя методы или переменные, не должен менять заложенную логику базового класса.
Например:
class Rectangle < private int width; private int height; public int getWidth() < return width; >public int getHeight() < return height; >public void setWidth(int width) < this.width = width; >public void setHeight(int height) < this.height = height; >> class Square implements Rectangle < public void setSize(int size) < super.setWidth(size); super.setHeight(size); >>
Выглядит немного странно, правда? Класс Square(Квадрат) наследуется от Rectangle(прямоугольник), а так как у квадрата все стороны равны, в классе Square мы просто задаем ширину и высоту, одну и ту же. В итоге, мы испортили изначальную идею класса Rectangle, в который заложена логика, что стороны могут отличаться. Получается, не меняя базовый класс, мы умудрились нарушить данный принцип.
В этом примере Square должен быть отдельным классом и ни в коем случае не наследоваться от Rectangle. То есть, поведение методов не должно изменяться. Если написано, что метод возвращает ширину, значит он и должен возвращать ширину, а не что-то другое.
I — Interface segregation principle
Принцип разделения интерфейса. Тут все очень просто. Лучше создавать много отдельных узкоспециализированных интерфейсов, чем один, который включает в себя много функций. Это позволит сделать архитектуру более гибкой, и позволит использовать интерфейсы по отдельности. Этот принцип похож на самый первый, принцип единой ответственности.
Например:
interface ItemClick
Итак, есть интерфейс, который требует реализовать два метода: короткое нажатие и длинное нажатие. Но что, если нам необходимо только короткое нажатие? В этом случае у нас будет, что-то такое:
class MyClass implements ItemClick < void onClick() < // тут реализуем необходимую логику >void onLongClick() < // а вот тут нам нечего не надо делать, но все равно приходится // реализовать этот метод потому, что этого требует интерфейс >>
Что-бы этого избежать необходимо сделать два разных интерфейса, которые можно применять по отдельности.
interface ItemClick < void onClick() >interface ItemLongClick
Так же посмотрите на пример, который был в статье про Композицию, мы создали новый интерфейс, вместо того, чтобы расширять существующий. Это позволяет использовать эти интерфейсы, как отдельно, так и вместе, что делает архитектуру более гибкой и изменяемой. Если бы мы создали новый метод в существующем классе, то нам пришлось бы реализовать его в классе, который его уже использует, а это значит, мы вмешиваемся в существующий код.
D — Dependency Inversion Principle
Модули верхних уровней не должны зависеть от модулей нижних уровней. Если по простому, то нужно делать код так, что-бы этот код имел как можно меньше зависимостей и не было круговых зависимостей, например модуль A зависит от модуля B, а модуль B зависит от модуля A.
Например, у вас есть следующие модули в приложении: presentation(модуль для показа чего либо на экране) и data(модуль для хранения и получения данных). Соответственно presentation будет зависеть от модуля data, так как ему необходимо получать данные для отображения их на экране, но вот модулю data совсем не нужно ничего знать о presentation, ему абсолютно все равно, как эти данные будут отображаться, модуль data просто предоставляет данные.
Saved searches
Use saved searches to filter your results more quickly
Cancel Create saved search
You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session. You switched accounts on another tab or window. Reload to refresh your session.
☕ Пять основных принципов дизайна классов (S.O.L.I.D.) в Java ☕
IvanSavchuk/S.O.L.I.D.
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Switch branches/tags
Branches Tags
Could not load branches
Nothing to show
Could not load tags
Nothing to show
Name already in use
A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
Cancel Create
- Local
- Codespaces
HTTPS GitHub CLI
Use Git or checkout with SVN using the web URL.
Work fast with our official CLI. Learn more about the CLI.
Sign In Required
Please sign in to use Codespaces.
Launching GitHub Desktop
If nothing happens, download GitHub Desktop and try again.
Launching GitHub Desktop
If nothing happens, download GitHub Desktop and try again.
Launching Xcode
If nothing happens, download Xcode and try again.
Launching Visual Studio Code
Your codespace will open once ready.
There was a problem preparing your codespace, please try again.
Latest commit
Git stats
Files
Failed to load latest commit information.
Latest commit message
Commit time
README.md
Пять основных принципов дизайна классов (S.O.L.I.D.) в Java
Классы — это блоки, из которых строится приложение Java. И если материал, из которого построено здание — некачественный, рано или поздно для такого здания настанут трудные времена. Так и в Java — некачественно написанные классы однажды могут привести к трудной ситуации в процессе работы приложения.
С другой стороны, хорошо разработанные и качественно написанные классы могут ускорить процесс кодирования и уменьшить количество ошибок.
В этой статье я перечислю пять основных принципов дизайна классов в объектно-ориентированном проектировании, которые следует иметь ввиду при написании кода. Сокращенно они называются S.O.L.I.D., и являются достаточно важными элементами написания классов.
Итак, как же расшифровывается S.O.L.I.D.:
- Single Responsibility Principle (Принцип единственной обязанности)
- Open Closed Principle (Принцип открытости/закрытости)
- Liskov’s Substitution Principle (Принцип подстановки Барбары Лисков)
- Interface Segregation Principle (Принцип разделения интерфейса)
- Dependency Inversion Principle (Принцип инверсии зависимостей)
Принцип единственной обязанности
Суть принципа ясна из одной-единственной фразы: На каждый объект должна быть возложена одна единственная обязанность. Другими словами, вы должны писать, изменять и поддерживать класс только для одной цели. Если это модель класса, то она должна строго представлять собой одну функцию или действие. Это даст вам возможность вносить изменения в будущем, не боясь влияния изменений на другие объекты.
Каждый объект должен иметь одну обязанность и эта обязанность должна быть полностью инкапсулирована в класс. Все его сервисы должны быть направлены исключительно на обеспечение этой обязанности.
Например, представьте себе модуль, который составляет и печатает отчёт. Такой модуль может измениться по двум причинам. Во-первых, может измениться само содержимое отчёта. Во-вторых, может измениться формат отчёта. Оба этих фактора изменяют модуль по разным причинам: в одном случае изменение содержательное, а во втором — косметическое. Принцип единственной обязанности говорит, что оба аспекта этой проблемы на самом деле являются двумя разными обязанностями, и в таком случае должны находиться в разных классах или модулях. Объединение двух сущностей, изменяющихся по разным причинам и в разное время, считается плохим проектным решением.
Этот принцип достаточно емко характеризует следующее положение: Программные сущности (классы, модули, функции и т.п.) должны быть открыты для расширения, но закрыты для изменения. Что это значит? Это означает, что классы должны быть разработаны таким образом, что чтобы подстроить класс к данным конкретным условиям применения, достаточно расширить класс и переопределить некоторые функции. Таким образом, система должна быть гибкой и иметь возможность работы в изменяющихся условиях без изменения исходного кода. Если другие разработчики не в состоянии спроектировать желаемое поведение из-за ограничений в вашем классе, то вам следует изменить его. Это не значит, что любой может изменить всю логику вашего класса, но он/она должен иметь расширить класс и подстроить его для выполнения конкретной задачи.
Принцип подстановки Барбары Лисков
Этот принцип является вариацией принципа открытости/закрытости, о котором говорилось ранее. В нем говорится: Объекты в программе могут быть заменены их наследниками без изменения свойств программы. Это означает, что класс, разработанный на основании вашего базового класса путем расширения, должен работать в приложении без сбоев. То есть, если разработчик расширяет ваш класс и использует его в приложении, он не должен нарушать работу приложения или создавать фатальные ошибки для всего приложения.
Этого легко добиться, если помнить одно простое правило: Если ваш базовый класс делает строго одно дело, разработчик получит при использовании класса только одну проблему. Это может привести к некоторым ошибкам в одной области, но все приложение не будет работать неправильно.
Принцип разделения интерфейса
Характеризуется следующим утверждением: Клиенты не должны быть вынуждены реализовывать ненужные методы, которые они не будут использовать
Принцип разделения интерфейсов говорит о том, что слишком «толстые» интерфейсы необходимо разделять на более маленькие и специфические, чтобы клиенты маленьких интерфейсов знали только о методах, которые необходимы им в работе. В итоге, при изменении метода интерфейса не должны меняться клиенты, которые этот метод не используют. Рассмотрим пример. Разработчик Алекс создал интерфейс «отчет», и добавил два метода: generateExcel() и generatedPdf(). Теперь клиент А хочет использовать этот интерфейс, но он намерен использовать отчеты только в PDF формате, а не в Excel. Устроит ли его такая функциональность?
Нет. Он должен будет реализовать два метода, один из которых по большому счету не нужен, и существует только благодаря Алексу — дизайнеру программного обеспечения. Клиент воспользуется либо другим интерфейсом, либо оставит поле для Excel пустым.
Так в чем же решение? Решение состоит в разделении существующего интерфейса на два более мелких. Один — отчет в формате PDF, второй — отчет в формате Escel. Это даст пользователю возможность использовать только необходимый для него функционал.
Принцип инверсии зависимостей
Многие знают слова, характеризующие этот принцип: Зависимости внутри системы строятся на основе абстракций. Модули верхнего уровня не зависят от модулей нижнего уровня. Абстракции не должны зависеть от деталей. Детали должны зависеть от абстракций. Другими словами, следует разрабатывать программное обеспечение таким образом, что различные модули были автономными, и соединялись друг с другом с помощью абстракции. Классическое использование этого принципа можно наблюдать на примере Spring framework. В рамках Spring framework все модули выполнены в виде отдельных компонентов, которые могут работать вместе. Они выполнены настолько автономно, что могут использоваться в других программных модулях, кроме Spring framework с такой же легкостью.
Это было достигнуто путем инверсии зависимости закрытых и открытых принципов. Все модули предоставляют доступ лишь к абстракции, которая может использоваться в другом модуле.
Итак, это были пять основных принципов дизайна классов в объектно-ориентированном проектировании, которые следует иметь ввиду при написании кода. Удачи!
SOLID принципы в Java
SOLID (от англ. single responsibility, open–closed, Liskov substitution, interface segregation и dependency inversion) – это набор принципов написания программного кода, при выполнении которых код будет удобно поддерживать и масштабировать.
Пройдя обучающий курс «SOLID принципы в Java», вы поймете основы написания чистого и красивого кода Java. На курсе вы сначала рассмотрите плохие примеры написания программного кода, а затем изучите способы и принципы того, как на практике превратить код из плохого и неоптимального в красивый и чистый.
На данном курсе будет представлен детальный обзор принципа единой обязанности, открытости и закрытости, подстановки Лесков, разделения интерфейса и инверсии зависимостей. К каждому уроку будут приведены примеры в коде на языке Java, разбор нечистого кода, рефакторинг и домашние задания на закрепление материала. После прохождения курса вы будете писать чистый код, который будет соответствовать принципам SOLID.
Читать дальше.
Предварительные Требования
Курс рассчитан на новичков, желающих познакомиться с основами использования принципов SOLID в Java и научиться основам правильной организации кода. Также курс будет полезен работающим специалистам, желающим обновить в памяти знание этих принципов.
Читать дальше.
Вы научитесь
- Понимать проблемы, решаемые использованием SOLID.
- Оптимизировать существующий или писать новый чистый код в соответствии с принципами SOLID.
- Понимать проблемы от несоблюдения принципа единой обязанности.
- Сопоставлять примеры открытости и закрытости для понимания оптимальности кода.
- Понимать формулировку принципа разделения интерфейса и использовать его для рефакторинга.
- Понимать принцип подстановки Лесков и проблему несоблюдения принципа.
- Понимать разницу между Dependency Inversion и Dependency Injection.
- Без проблем объяснить, что значит каждый из принципов.
SOLID — принципы объектно‑ориентированного программирования
SOLID — это аббревиатура пяти основных принципов проектирования в объектно‑ориентированном программировании — Single responsibility, Open-closed, Liskov substitution, Interface segregation и Dependency inversion.
В переводе на русский: принципы единственной ответственности, открытости / закрытости, подстановки Барбары Лисков, разделения интерфейса и инверсии зависимостей)
Аббревиатура SOLID была предложена Робертом Мартином, автором нескольких книг, широко известным в сообществе разработчиков. Следование принципам позволяет строить на базе ООП масштабируемые и сопровождаемые программные продукты с понятной бизнес‑логикой. Код, который написан с соблюдением принципов SOLID, проще понимать, поддерживать, расширять или изменять его функциональность.
Расшифровка:
- Single responsibility — принцип единственной ответственности
- Open-closed — принцип открытости / закрытости
- Liskov substitution — принцип подстановки Барбары Лисков
- Interface segregation — принцип разделения интерфейса
- Dependency inversion — принцип инверсии зависимостей
Принцип единственной обязанности / ответственности (single responsibility principle / SRP) обозначает, что каждый объект должен иметь одну обязанность и эта обязанность должна быть полностью инкапсулирована в класс. Все его сервисы должны быть направлены исключительно на обеспечение этой обязанности. Подробнее про SRP →
Принцип открытости / закрытости (open-closed principle / OCP) декларирует, что программные сущности (классы, модули, функции и т. п.) должны быть открыты для расширения, но закрыты для изменения. Это означает, что эти сущности могут менять свое поведение без изменения их исходного кода. Подробнее про OCP →
Принцип подстановки Барбары Лисков (Liskov substitution principle / LSP) в формулировке Роберта Мартина: «функции, которые используют базовый тип, должны иметь возможность использовать подтипы базового типа не зная об этом». Подробнее про LSP →
Принцип разделения интерфейса (interface segregation principle / ISP) в формулировке Роберта Мартина: «клиенты не должны зависеть от методов, которые они не используют». Принцип разделения интерфейсов говорит о том, что слишком «толстые» интерфейсы необходимо разделять на более маленькие и специфические, чтобы клиенты маленьких интерфейсов знали только о методах, которые необходимы им в работе. В итоге, при изменении метода интерфейса не должны меняться клиенты, которые этот метод не используют. Подробнее про ISP →
Принцип инверсии зависимостей (dependency inversion principle / DIP) — модули верхних уровней не должны зависеть от модулей нижних уровней, а оба типа модулей должны зависеть от абстракций; сами абстракции не должны зависеть от деталей, а вот детали должны зависеть от абстракций. Подробнее про DIP →
Статья опубликована в 2019 и была обновлена в 2023 году
Тематические статьи
Принцип программирования YAGNI — «Вам это не понадобится»
Принцип заключается в том, что возможности, которые не описаны в требованиях к системе, просто не должны реализовываться.
В результате разработка ненужных функций не сжигает бюджет проекта, а разработчики не тратят оплачиваемое время на реализацию и дальнейшее сопровождение в реальности ненужного функционала. Избыточный функционал сжигает больше всего ресурсов именно на сопровождении: больше написанного кода — труднее сопровождать и выше вероятность появления «багов». И тут очень уместна поговорка: «лучший код — это ненаписанный код».
методологии разработки
веб-разработка
Статья опубликована в 2019 и обновлена в 2023 году
Принцип программирования KISS — делайте вещи проще
KISS — это принцип проектирования и программирования, при котором простота системы декларируется в качестве основной цели или ценности.
Большая часть программных систем необосновано перегружена практически ненужными функциями, что ухудшает удобство их использование конечными пользователями, а также усложняет их поддержку и развитие разработчиками. Следование принципу KISS позволяет разрабатывать решения, которые не обладают этими недостатками: они просты в использовании и в сопровождении.
методологии разработки
веб-разработка
Статья опубликована в 2019 и обновлена в 2023 году
Принцип программирования DRY — don’t repeat yourself / не повторяйте себя
Следование принципу DRY позволяет добиться высокой сопровождаемости программного продукта: внесение изменений и тестирование значительно упрощаются.
Если код не дублируется, то для изменения логики достаточно внесения исправлений всего в одном месте. Также значительно проще тестировать одну (пусть и более сложную) функцию, а не набор из десятков однотипных. При следовании DRY упрощается и повторное использование функций, вынесенных из сложных алгоритмов, что позволяет сократить время разработки и тестирования новой функциональности.
веб-разработка
методологии разработки
Статья опубликована в 2018 и обновлена в 2023 году
Стандарты кодирования — залог хорошей сопровождаемости проекта
Любая командная разработка может быть эффективной только в том случае, если участники команды имеют общее видение.
Если над проектом работает команда, а не один‑два разработчика, то обязательно должен быть стандарт оформления кода — набор правил и соглашений, которые описывают базовые принципы оформления программного кода, используемого совместно группой разработчиков.
методологии разработки
веб-разработка
Статья опубликована в 2014 и обновлена в 2023 году
Флаги функций (Feature Flags)
Флаги функций позволяют отделить развертывание функций от развертывания кода, обеспечивают возможности для A/B-тестирования и предоставляют механизм быстрого отключения проблемных функций