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 состоят из 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

Источник

Шпаргалка по SOLID-принципам с примерами на PHP

Тема SOLID-принципов и в целом чистоты кода не раз поднималась на Хабре и, возможно, уже порядком изъезженная. Но тем не менее, не так давно мне приходилось проходить собеседования в одну интересную IT-компанию, где меня попросили рассказать о принципах SOLID с примерами и ситуациями, когда я не соблюл эти принципы и к чему это привело. И в тот момент я понял, что на каком-то подсознательном уровне я понимаю эти принципы и даже могут назвать их все, но привести лаконичные и понятные примеры для меня стало проблемой. Поэтому я и решил для себя самого и для сообщества обобщить информацию по SOLID-принципам для ещё лучшего её понимания. Статья должна быть полезной, для людей только знакомящихся с SOLID-принципами, также, как и для людей «съевших собаку» на SOLID-принципах.

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

Что же такое SOLID-принципы? Если верить определению Wikipedia, это:

аббревиатура пяти основных принципов дизайна классов в объектно-ориентированном проектировании — Single responsibility, Open-closed, Liskov substitution, Interface segregation и Dependency inversion.

Принцип единственной ответственности (Single responsibility)

Итак, в качества примера возьмём довольно популярный и широкоиспользуемый пример — интернет-магазин с заказами, товарами и покупателями.

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

Рассмотрим следующее описание класса для представления заказа в интернет-магазине:

Как можно увидеть, данный класс выполняет операций для 3 различный типов задач: работа с самим заказом( calculateTotalSum, getItems, getItemsCount, addItem, deleteItem ), отображение заказа( printOrder, showOrder ) и работа с хранилищем данных( load, save, update, delete ).
К чему это может привести?
Приводит это к тому, что в случае, если мы хотим внести изменения в методы печати или работы хранилища, мы изменяем сам класс заказа, что может привести к его неработоспособности.
Решить эту проблему стоит разделением данного класса на 3 отдельных класса, каждый из которых будет заниматься своей задачей

Теперь каждый класс занимается своей конкретной задачей и для каждого класса есть только 1 причина для его изменения.

Принцип открытости/закрытости (Open-closed)

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

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

Очевидно, что такой код явно выполняется не так, как от него этого ждут.
Но в чём проблема? Разве «квадрат» не является «прямоугольником»? Является, но в геометрических понятиях. В понятиях же объектов, квадрат не есть прямоугольник, поскольку поведение объекта «квадрат» не согласуется с поведением объекта «прямоугольник».

«Что ещё за пред- и постусловия?» — можете спросите Вы.
Ответ: предусловия – это то, что должно быть выполнено вызывающей стороной перед вызовом метода, постусловия – это то, что, гарантируется вызываемым методом.

Поэтому, лучше в рамках ООП и задачи расчёта площади фигуры не делать иерархию «квадрат» наследует «прямоугольник», а сделать их как 2 отдельные сущности:

Хороший реальный пример несоблюдения принципа Лискоу и решения, принятого в связи с этим, рассмотрен в книге Роберта Мартина «Быстрая разработка программ» в разделе «Принцип подстановки Лискоу. Реальный пример».

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

Данный принцип гласит, что «Много специализированных интерфейсов лучше, чем один универсальный»
Соблюдение этого принципа необходимо для того, чтобы классы-клиенты использующий/реализующий интерфейс знали только о тех методах, которые они используют, что ведёт к уменьшению количества неиспользуемого кода.

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

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

Принцип инверсии зависимостей (Dependency Invertion)

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

Для примера рассмотрим оплату заказа покупателем.

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

Шпаргалка

Надеюсь, моя «шпаргалка» поможет кому-нибудь в понимании принципов SOLID и даст толчок к их использованию в своих проектах.
Спасибо за внимание.

Источник

Почему SOLID – важная составляющая мышления программиста. Разбираемся на примерах с кодом

Привет! Меня зовут Иван, я сотрудничаю со львовским офисом EPAM как Solution Architect, а карьеру в IT начал 10 лет назад. За это время заметил, что многие любят работать на проектах, которые начинаются с нуля. Однако не всем удается построить систему, которую будет все еще легко поддерживать и развивать спустя год.

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

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

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

solid php что такое. 7fcruhytjkbwblk0awpok2bvc2u. solid php что такое фото. solid php что такое-7fcruhytjkbwblk0awpok2bvc2u. картинка solid php что такое. картинка 7fcruhytjkbwblk0awpok2bvc2u. Disclaimer: Всем можно, ну а я чем хуже?!

Зачем нужен SOLID

SOLID – набор принципов объектно-ориентированного программирования, который представил Роберт Мартин в 1995 году. Их идея состоит в необходимости избегать зависимостей между компонентами кода. Код с большим количеством зависимостей (т.н. «спагетти-код») поддерживать сложно. Основные проблемы такого кода:

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

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

Например, класс User. Его ответственность – предоставлять информацию о пользователе: имя, e-mail и тип подписки, которую он использует в сервисе.

Рассмотрим метод hasUnlimitedContentAccess. На основе типа подписки он определяет есть ли у пользователя неограниченный доступ к контенту. Но разве делать такой вывод – ответственность класcа User?

Получается, что у класса User есть две цели существования: предоставлять информацию о пользователе и делать вывод об уровне доступа к контенту на основе подписки. Это нарушает принцип Single Responsibility.

Почему существование метода hasUnlimitedContentAccess в классе User имеет негативные последствия? Потому что контроль за типом подписки «расплывается» по всей программе. Кроме класса User могут быть классы MediaLibrary и Player, которые на основе этих же данных тоже будут решать, что им делать. Каждый класс трактует значение типа подписки по-своему. Если правила имеющихся подписок изменятся, необходимо будет обновить все классы, так как каждый выстроил свой набор правил работы с ними.

Удалим метод hasUnlimitedContentAccess в классе User и создадим новый класс, который будет отвечать за работу с подписками.

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

Single Responsibility Principle касается не только уровня классов – модули классов также необходимо проектировать таким образом, чтобы они были узкоспециализированы.

Кроме SOLID существует и другой набор принципов проектирования программного обеспечения – GRASP. Некоторые его принципы пересекаются с SOLID. Если же говорить о Single Responsibility Principle, то с GRASP можно сопоставить:

Приницип открытости/закрытости (Open/Close Principle)

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

Наверное, каждый из нас видел бесконечные цепочки if then else или switch. Как только добавляется очередное условие, мы пишем очередной if then else, меняя при этом сам класс. Либо класс выполняет процесс с множеством последовательных шагов – и каждый новый шаг приводит к его изменению. А это нарушает Open/Close Principle.

Рассмотрим несколько способов расширения класса без его непосредственного изменения.

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

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

А что делать, если мы не можем добавлять методы к фигурам? Существуют методы, которые противоречат Single Responsibility Principle. Тогда можно воспользоваться шаблоном проектирования «Стратегия» (Strategy): создать множество похожих алгоритмов и вызывать их по определенному ключу.

Кроме того, стратегии можно регистрировать в Inversion of Control контейнере. В этом случае класс, нуждающийся в них, получит стратегии на этапе создания автоматически.

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

Используем дизайн-паттер «Конвейер» (Pipeline)

Теперь, если необходимо изменить способ обработки изображения, мы модицифируем массив с методами. Сам класс ImageProcessor остается неизменным. Представьте, что есть необходимость обрабатывать разные изображения по-разному. Вместо того, чтобы писать разные версии ImageProcessor, иначе скомбинируем нужные нам методы в массиве pipeMethods.

Еще несколько преимуществ. Ранее мы добавляли новый метод обработки изображений прямо в ImageProcessor, из-за чего возникала необходимость добавлять новые зависимости. Например, метод highlightLetters требует дополнительную библиотеку для поиска символов на изображении. Соответственно, больше методов – больше зависимостей. Сейчас каждый PipeMethod можно разработать в отдельном модуле и подключать только необходимые зависимости.

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

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

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

Принципы GRASP, общие с Open/Close Principle:

Принцип подстановки Лисков (Liskov Substitution Principle)

Если объект базового класса заменить объектом его производного класса, то программа должна продолжить работать корректно.

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

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

Рассмотрим следующую иерархию транспортных средств:

Все работает до момента, когда мы добавляем новый класс – Поезд.

Поскольку поезд не может произвольно менять направление своего движения, то turn родительского класса будет нарушать принцип подстановки Лисков.

Чтобы исправить ситуацию, мы можем добавить два родительских класса: FreeDirectionalVehicle – будет разрешить произвольное направление движения и BidirectionalVehicle — движение только вперед и назад. Теперь все классы будут наследовать лишь те методы, которые смогут обеспечить.

Кроме того, класс-потомок не должен добавлять какие-либо условия до и после выполнения метода. Например:

В этой иерархии мы не сможем легко заменить объекты родительского класса Logger объектами TcpLogger, т.к. до и после вызова метода нам необходимо дополнительно вызвать openConnection и closeConnection. Получается, мы накладываем 2 дополнительных условия на вызов метода log, что также нарушает принцип подстановки Лисков.

Чтобы решить ситуацию выше, мы можем сделать методы openConnection и closeConnection приватными. В методе log класса TcpLogger организуем запись логов в файл. С периодичностью (например, каждую минуту) будем открывать соединение, отправлять файл с логами и закрывать соединение. Дополнительно необходимо убедиться, что прежде, чем программа будет закрыта, мы отправили все логи. Если программа была завершена аварийно, можем отправить логи во время следующего ее запуска.

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

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

Так как с файла мы читаем локально, метод Connect лишний. Разделим общий интерфейс IDataSource:

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

Принцип инверсии зависимостей (Dependency Inversion Principle)

Данный принцип состоит из двух утверждений:

Наш модуль верхнего уровня UserService использует детали реализации трех модулей нижнего уровня: localStorage, fetch и Date. Такой подход плох тем, что если мы, например, вместо fetch решим использовать библиотеку, которая делает HTTP-запросы, то придется переписывать UserService. Кроме того, такой код сложно покрыть тестами.

Еще одним нарушением является то, что из метода getUser мы возвращаем реализованный класс User, а не его абстракцию – интерфейс IUser.

Создадим абстракции, с которыми было бы удобно работать внутри модуля UserService.

Как видим, код стал гораздо проще, его легко тестировать. Теперь взглянем на реализацию интерфейсов ICache и IRemoteService.

Мы сделали враперы над localStorage и fetch. Важным моментом в реализации двух классов является то, что мы не используем localStorage и fetch напрямую. Мы все время работаем с созданными для них интерфейсами. LocalStorage и fetch будут передаваться в конструктор, если там не будет указано никаких параметров. Для тестов же можно создать mocks или stubs, которые заменят localStorage или fetch, и передать их как параметры в конструктор.

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

Выводы

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

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

И в заключение: отца-основателя SOLID, Роберта Мартина, небезосновательно считают настоящей рок-звездой в мире разработки ПО. На его книгах уже выросло не одно поколение суперуспешных программистов. «Clean Code» и «Clean Coder» — две его книги о том, как писать качественный код и соответствовать высочайшим стандартам в индустрии. Думаю, многие из вас уже успели причитать хотя бы одну из них, а если нет, то у вас есть неплохой шанс прокачать свой уровень разработчика.

Источник

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *