solid php что такое

Очень простое объяснение принципов SOLID

Disclaimer: Всем можно, ну а я чем хуже?!

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

Попробуем разобраться в этих принципах на пальцах, без примеров кода и СМС.

S — Принцип единственной ответственности (Single Responsibility Principle или SRP)

Должна быть одна и только одна причина для изменения класса («A class should have only one reason to change.» Robert C. Martin.)

Представим, что у вас на компе лежит пяток любимых песен, пара фильмов и фотки котиков. Вы валите все это в «Мои документы» и, в целом, радуетесь жизни.

Потом вы качаете еще фильмов, новый альбом любимой группы и десяток новых котиков. В «Моих документах» становится как-то не комфортно и вы раскладываете все по папочкам.

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

И что мы тут имеем?

Какие можно сделать выводы?

O — Принцип открытости/закрытости (Open-closed Principle или OCP)

Программные сущности (классы, модули, функции и т.п.) должны быть открыты для расширения, но закрыты для изменения («Software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification.» Bertrand Meyer)

Вернемся к тому шагу нашей истории, когда вы подключили скоростной безлимит. Допустим, что в первую очередь вы накачали всевозможных фильмом про сантехников кинокомпании Private и, так как они все были про сантехников, создали папку «/Видео/Про сантехников/».

Через некоторое время вы скачали еще фильмов этой студии, но уже про доставку пиццы. Мало того, ваша новая девушка написала вам СМС «Я скачала фильм „Афоня“ и сохранила его в папку /Видео/Про сантехников/, ок?». И вроде бы все верно — про сантехника, но есть нюанс.
И тут вам становится понятно, что изменение функциональности невозможно без модификации уже существующей структуры папок(классов), а это явное нарушение принципа OCP.

Как в этом случае надо было сделать? Очень просто — изначально спроектировать систему таким образом, чтобы новый функционал не требовал изменения старого кода. Т.е. не хардкодить папку «../Про сантехников/», надеясь, что в будущем там только про них и будет, а повысить уровень абстракции до «../Студия Private/» и спокойно скармливать ей и сантехников, и разносчиков пиццы, и прочая-прочая…

А для Афони создать новый класс, например «../Мосфильм/», расширив класс «/Видео/»

L — Принцип подстановки Барбары Лисков (Liskov Substitution Principle или LSP)

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

Ну тут совсем просто.

Для объяснения давайте обратимся к милоте и няшности, а конкретно к папке «/Фото/Котики/».
Котиков мы любим и фотографий собрано у нас очень много.

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

Невозможно отобразить файл «/Фото/Котики/Книги/Кот в сапогах.fb2»

Как потом выяснилось, ваша подруга решила поднять градус милоты и отнаследовала «Котики» новой подпапкой «Книги», грубо нарушив LSP, так как подкласс «Книги» ну никак нельзя использовать вместо базового класса «Фото».

I — Принцип разделения интерфейса (Interface Segregation Principle или ISP)

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

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

Вам надоел стандартный проигрыватель музыки и вы решили скачать новый, супермодный и хайповый. Он даже умеет отображать обложку музыкального альбома и выводить субтитры исполняемой песни. Но вот беда, если в папке альбома отсутствуют файлы «cover.jpg» и «subtitles.txt», то проигрыватель падает с ошибкой. И вот вы, проклиная все на свете, начинаете создавать во всех подпапках с альбомами эти файлы заглушки.

Т.е., проводя некорректные аналогии, мы обязали класс «Музыка» и всех его наследников реализовывать интерфейс «AudioCoverSubtitles». При этом, полностью этот интерфейс реализует только альбом «Waiting for the Sun», альбом «The best of The Doors» реализует только часть «Audio+Cover», а все остальные только «Audio».

Это подводит нас к мысли, что имеет смысл разделить толстый интерфейс «AudioCoverSubtitles» на три небольших «Audio», «Cover» и «Subtitles» и применять их только там, где они действительно нужны.

D — Принцип инверсии зависимостей (dependency inversion principle или DIP)

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

Абстракции не должны зависеть от деталей. Детали должны зависеть от абстракций.

Модуль «The Doors», его подмодуль «Waiting for the Sun» (и все остальные), зависят от верхнеуровневой абстракции «Музыка», которая определяет их реализацию (то, что внутри у них музыка).

Допустим, мы решили разделить хранение музыки по принципу сжатия — «с потерями» и «без потерь». Это детали, которые объединяет зависимость от абстракции «Музыка» — в них, в конечном итоге, должна таки быть музыка. При этом сама абстракция «Музыка» не зависит от этих деталей. Ей все равно, с потерями там музыка или без них — она как была музыкой, так ей и остается.

Источник

Принципы SOLID, о которых должен знать каждый разработчик

Объектно-ориентированное программирование принесло в разработку ПО новые подходы к проектированию приложений. В частности, ООП позволило программистам комбинировать сущности, объединённые некоей общей целью или функционалом, в отдельных классах, рассчитанных на решение самостоятельных задач и независимых от других частей приложения. Однако само по себе применение ООП не означает, что разработчик застрахован от возможности создания непонятного, запутанного кода, который тяжело поддерживать. Роберт Мартин, для того, чтобы помочь всем желающим разрабатывать качественные ООП-приложения, разработал пять принципов объектно-ориентированного программирования и проектирования, говоря о которых, с подачи Майкла Фэзерса, используют акроним SOLID.

Читайте также:  какие упражнения для дыхания легких

Что такое SOLID?

Вот как расшифровывается акроним SOLID:

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

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

«Одно поручение. Всего одно.» — Локи говорит Скурджу в фильме «Тор: Рагнарёк».
Каждый класс должен решать лишь одну задачу.

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

Обратите внимание на то, что этот принцип применим не только к классам, но и к компонентам программного обеспечения в более широком смысле.

Например, рассмотрим этот код:

Как такая структура класса может привести к проблемам?

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

Для того чтобы привести вышеприведённый код в соответствие с принципом единственной ответственности, создадим ещё один класс, единственной задачей которого является работа с хранилищем, в частности — сохранение в нём объектов класса Animal :

Вот что по этому поводу говорит Стив Фентон: «Проектируя классы, мы должны стремиться к тому, чтобы объединять родственные компоненты, то есть такие, изменения в которых происходят по одним и тем же причинам. Нам следует стараться разделять компоненты, изменения в которых вызывают различные причины».

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

Принцип открытости-закрытости

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

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

Добавим в массив новый элемент:

После этого нам придётся поменять код функции AnimalSound :

Как привести функцию AnimalSound в соответствие с принципом открытости-закрытости? Например — так:

Если теперь добавить в массив объект, описывающий новое животное, функцию AnimalSound менять не придётся. Мы привели её в соответствие с принципом открытости-закрытости.

Рассмотрим ещё один пример.

Представим, что у нас есть магазин. Мы даём клиентам скидку в 20%, используя такой класс:

Теперь решено разделить клиентов на две группы. Любимым ( fav ) клиентам даётся скидка в 20%, а VIP-клиентам ( vip ) — удвоенная скидка, то есть — 40%. Для того, чтобы реализовать эту логику, было решено модифицировать класс следующим образом:

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

Если решено дать скидку в 80% «супер-VIP» клиентам, выглядеть это должно так:

Как видите, тут используется расширение возможностей классов, а не их модификация.

Принцип подстановки Барбары Лисков

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

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

Функция нарушает принцип подстановки (и принцип открытости-закрытости). Этот код должен знать о типах всех обрабатываемых им объектов и, в зависимости от типа, обращаться к соответствующей функции для подсчёта конечностей конкретного животного. Как результат, при создании нового типа животного функцию придётся переписывать:

Для того чтобы эта функция не нарушала принцип подстановки, преобразуем её с использованием требований, сформулированных Стивом Фентоном. Они заключаются в том, что методы, принимающие или возвращающие значения с типом некоего суперкласса ( Animal в нашем случае) должны также принимать и возвращать значения, типами которых являются его подклассы ( Pigeon ).

Вооружившись этими соображениями мы можем переделать функцию AnimalLegCount :

Теперь в классе Animal должен появиться метод LegCount :

А его подклассам нужно реализовать этот метод:

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

Принцип разделения интерфейса

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

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

Рассмотрим интерфейс Shape :

Он описывает методы для рисования кругов ( drawCircle ), квадратов ( drawSquare ) и прямоугольников ( drawRectangle ). В результате классы, реализующие этот интерфейс и представляющие отдельные геометрические фигуры, такие, как круг (Circle), квадрат (Square) и прямоугольник (Rectangle), должны содержать реализацию всех этих методов. Выглядит это так:

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

Читайте также:  windows uefi mode что это

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

Теперь интерфейс ICircle используется лишь для рисования кругов, равно как и другие специализированные интерфейсы — для рисования других фигур. Интерфейс Shape может применяться в качестве универсального интерфейса.

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

Объектом зависимости должна быть абстракция, а не что-то конкретное.

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

Здесь класс Http представляет собой высокоуровневый компонент, а XMLHttpService — низкоуровневый. Такая архитектура нарушает пункт A принципа инверсии зависимостей: «Модули верхних уровней не должны зависеть от модулей нижних уровней. Оба типа модулей должны зависеть от абстракций».

Класс Http не должен знать о том, что именно используется для организации сетевого соединения. Поэтому мы создадим интерфейс Connection :

Интерфейс Connection содержит описание метода request и мы передаём классу Http аргумент типа Connection :

Перепишем класс XMLHttpService таким образом, чтобы он реализовывал этот интерфейс:

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

Итоги

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

Еще больше полезной информации для программистов вы найдете на нашем сайте.

Источник

SOLID: 5 принципов объектно-ориентированного программирования

By Samuel Oloruntoba

Published on February 19, 2021

Введение

SOLID — это аббревиатура, обозначающая первые пять принципов объектно-ориентированного программирования, сформулированные Робертом С. Мартином (также известным как дядя Боб).

Примечание. Хотя эти принципы применимы к разным языкам программирования, в этой статье мы приведем примеры для языка PHP.

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

SOLID включает следующие принципы:

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

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

Принцип единственной ответственности (SRP) гласит:

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

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

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

В случае квадратов необходимо знать длину стороны:

В случае кругов необходимо знать радиус :

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

Вот пример с набором из трех фигур:

Проблема с методом вывода заключается в том, что класс AreaCalculator использует логику для вывода данных.

Давайте рассмотрим сценарий, в котором вывод необходимо конвертировать в другой формат, например, JSON.

Для решения этой проблемы вы можете создать отдельный класс SumCalculatorOutputter и использовать этот новый класс для обработки логики, необходимой для вывода данных пользователю:

Класс SumCalculatorOutputter должен работать следующим образом:

Это соответствует принципу единственной ответственности.

Принцип открытости/закрытости

Принцип открытости/закрытости гласит:

Объекты или сущности должны быть открыты для расширения, но закрыты для изменения.

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

Давайте вернемся к классу AreaCalculator и посмотрим на метод sum :

В этом случае метод sum класса AreaCalculator можно переписать так:

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

Кодирование в интерфейс является неотъемлемой частью принципов SOLID.

Вот обновление класса Square :

А вот обновление класса Circle :

В методе sum класса AreaCalculator вы можете проверить, являются ли фигуры экземплярами ShapeInterface ; а если это не так, программа выдаст исключение:

Это соответствует принципу открытости/закрытости.

Принцип подстановки Лисков

Принцип подстановки Лисков гласит:

Пусть q(x) будет доказанным свойством объектов x типа T. Тогда q(y) будет доказанным свойством объектов y типа S, где S является подтипом T.

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

Помните, что класс SumCalculatorOutputter выглядит примерно так:

Если мы попробуем выполнить такой пример:

Это соответствует принципу подстановки Лисков.

Принцип разделения интерфейса

Принцип разделения интерфейса гласит:

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

Так мы получим единый API для управления фигурами:

Это соответствует принципу разделения интерфейса.

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

Принцип инверсии зависимостей гласит:

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

Этот принцип открывает возможности разъединения.

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

В этом коде модули высокого уровня и модули низкого уровня зависят от абстракции.

Заключение

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

Читайте также:  Что такое нелегитимная власть

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

Источник

Принципы SOLID состоят из 5 ключевых идей по написанию и проектированию объектно-ориентированных приложений. Принцип SOLID, сама его идея появилась в 2000 году Робертом Мартином (однако, само официальное название этому принципу были утверждено только спустя несколько лет). Принципы, именуемые, как SOLID были настолько хороши, что спустя лишь небольшое время они захватили внимание сообщества программистов.

Так же, совутую, после изучения материала этой статьи (или даже во время), ознакомиться с принципами SOLID в картинках.

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

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

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

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

Посмотрите на пример плохо спреэктированного класса:

А в контроллере этот класс вызывается как-то так:

Вместо этого, любой из классов должен иметь только по одной обязанности

Потому, сейчас перепишем этот класс с учётом новых требований.

Другое изменение было применено к проверке аутентификации пользователя. Эта обязанность была вынесена в контроллер, где ему и место.

Новая версия выглядит на порядок чище, проще для тестирования, и обновления.

Принцип Открытости/Закрытости

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

Например, если вы когда-то использовали шаблонизатор Twig, то вы наверняка писали плагины и расширения для него (добавляя собственные функции или фильтры в шаблонизатор). И как раз, из-за следования этому принципу, вы, написав собственное расширение (по соответствующему интерфейсу), добавили новую функциональность без изменения кода самого шаблонизатора.

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

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

А, чтобы более чётко понимать, как работать с интерфейсами и абстрактыми классами, советую почитать мою предыдущую статью на эту тему.

Принцип подстановки Барбары Лисков

Принцип подстановки Барбары Лисков (именуемый как LISP) это важная концепция, придуманная ещё в далёком 1987-м. Я опишу этот принцип на основу определений, условного описания кода, вместо реализации большого примера с кодом (чтобы сделать этот пример легче для понимания, я буду использова определение, как «Клиентский класс»).

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

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

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

Принцип разделения интерфейса

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

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

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

Резюме

В этой статье я попытался лайтово, не особо нагромождая ваше внимание большими кусками кода описать принципы SOLID в PHP, сделать его описание и привести полной объяснения его сути. Для того, чтобы научиться писать по SOLID-у, вам нужно просто практиковаться писать всё больше и больше, постоянно держа в голове эти принципы и стараясь их внедрять в нужные места. После чего, полное понимание и умение их применять придёт само собой.

Subscribe to Блог php программиста: статьи по PHP, JavaScript, MySql

Get the latest posts delivered right to your inbox

Источник

Информ портал о технике и не только