Что такое cohesion и coupling связанность и связность
Cohesion и Coupling: отличия
Эта статья является переводом материала «Cohesion and Coupling: the difference».
Возможно, вы слышали рекомендацию, в которой говорится, что мы должны стремиться к достижению low coupling (низкой связанности) и high cohesion (высокого сцепления) при работе над кодовой базой. В этой статье хотелось бы обсудить, что на самом деле означает эта рекомендация, и взглянуть на некоторые примеры кода, иллюстрирующие ее. И также хочется провести границу между этими двумя идеями и показать различия в них.
Далее в статье я не буду переводить такие термины, как coupling и cohesion, чтобы не возникло недоразумений.
Cohesion и Coupling: отличия
В то время как coupling довольно интуитивное понятие (почти ни у кого нет трудностей с ним), тогда как cohesion труднее понять. Более того, различия между ними часто кажутся неясными. Это неудивительно: идеи, лежащие в основе этих терминов, схожи. Тем не менее, они действительно отличаются.
Cohesion представляет собой степень, в которой часть кодовой базы образует логически единую атомарную единицу (юнит).
Он также может указать на количество связей внутри некоторой кодовой единицы. Если число мало, то, вероятно, границы для блока выбраны неправильно, код внутри блока логически не связан.
Блок (юнит) здесь необязательно является классом. Это может быть метод, класс, группа классов или даже модуль: понятие cohesion (а также coupling) применимо на разных уровнях.
С другой стороны, coupling представляет собой степень взаимосвязи между блоками. Другими словами, это количество соединений между двумя или более блоками. Чем меньше число, тем ниже coupling.
По сути, высокий cohesion означает хранение связанных друг с другом частей кода в одном месте. В то же время низкий coupling заключается в максимально возможном разделении несвязанных частей кодовой базы.
Теоретически рекомендации выглядят довольно просто. Однако на практике вам нужно достаточно глубоко погрузиться в предметную модель вашего программного обеспечения, чтобы понять, какие части вашей кодовой базы на самом деле связаны.
Возможно, отсутствие объективности в этой рекомендации является причиной того, что ей часто так трудно следовать.
Этот принцип имеет прямое отношение к другому принципу: разделение проблем (Separation of Concerns). Эти две рекомендации очень похожи с точки зрения лучших практик, которые они предлагают.
Типы кода с точки зрения cohesion и coupling
Помимо кода, который одновременно имеет высокий cohesive и слабый coupling, существует, по крайней мере, еще три типа:
Давайте рассмотрим их.
1. Идеальным является код, который следует рекомендациям. У него слабый coupling и высокий cohesive. Мы можем проиллюстрировать такой код следующим образом:
На рисунке выше круги одного цвета представляют части кодовой базы, связанные друг с другом.
2. God Object (божественный объект) является результатом введения высокого cohesion и высокого coupling. Это анти-паттерн в основном означает один фрагмент кода, который выполняет всю работу сразу:
3. Третий тип имеет место, когда границы между различными классами или модулями выбраны плохо:
В отличие от God Object, код этого типа имеет границы. Проблема здесь в том, что они выбраны неправильно и часто не отражают фактическую семантику домена. Такой код довольно часто нарушает Single Responsibility Principle (Принцип единой ответственности).
4. Деструктивная развязка (decoupling)– самый интересный случай. Это может происходить, когда программист пытается так сильно развязать кодовую базу, что код полностью теряет фокус:
Последний тип заслуживает более подробного обсуждения.
Сohesion и Сoupling: подводные камни
Часто, когда разработчик пытается реализовать рекомендации по низкому coupling, высокому cohesion, он или она прикладывает слишком много усилий к реализации первой рекомендации (низкий coupling) и полностью забывает о другой. Это приводит к ситуации, когда код действительно разделен (decoupled), но в то же время не имеет четкой направленности. Его части настолько отделены друг от друга, что становится трудно или даже невозможно понять их значение. Эта ситуация называется деструктивной развязкой (destructive decoupling).
Давайте рассмотрим пример:
Этот код является результатом деструктивной развязки. Вы можете видеть, что, с одной стороны, класс Order полностью отделен от Product и даже от OrderLine. Он делегирует логику расчета специальному интерфейсу IOrderPriceCalculator; создание OrderLine выполняется фабрикой.
В то же время этот код совершенно бессвязный (incohesive). Классы, семантика которых тесно связана, теперь отделены друг от друга. Это довольно простой пример, поэтому скорее всего вы понимаете, что здесь происходит, но представьте, как сложно было бы понять такой код, описывающий какую-то незнакомую модель предметной области. В большинстве случаев отсутствие согласованности делает код нечитаемым.
Деструктивная развязка часто идет рука об руку с подходом «интерфейсы повсюду». То есть соблазн заменить каждый конкретный класс интерфейсом, даже если этот интерфейс не представляет собой абстракцию.
Итак, как бы мы переписали приведенный выше код? Как-то так:
Таким образом, мы восстановили связи между Order, OrderLine и Product. Этот код является кратким и цельным (cohesive).
Тут я немного не согласен с автором оригинала. Но скорее всего он просто не стал заморачиваться, так как это простой пример кода. Однако я все же хочу указать на этот спорный момент. Поскольку Order и Product могут быть разными агрегатами или даже находится в разных сервисах (микросервисах, если проект достаточно большой), то во время добавления OrderLine (метод AddLine) лучше не передавать объект product, а использовать его id.
Важно понимать связь между cohesion и coupling. Невозможно полностью разделить кодовую базу без нарушения ее согласованности. Точно так же невозможно создать полностью цельный (cohesive) код без введения ненужного coupling, но такое отношение встречается редко, потому что, в отличие от cohesion, концепция coupling более или менее интуитивно понятна.
Баланс между ними является ключом к созданию кодовой базы с высоким (но не полностью) cohesion и слабым coupling (но не полностью развязанным).
Сohesion и Сoupling на разных уровнях
Как упоминалось ранее, cohesion и coupling могут применяться на разных уровнях. Уровень класса наиболее очевиден, но он не единственный. Примером здесь может служить структура папок внутри проекта:
На первый взгляд, проект хорошо организован: есть отдельные папки для сущностей, фабрик и так далее. Однако ему не хватает cohesion.
Он попадает в 3-ю категорию на нашей диаграмме: плохо выбранные границы. Хотя внутренние компоненты проекта действительно слабо связаны, их границы не отражают их семантику.
Структура проекта с высоким cohesion (и слабым coupling) была бы следующая:
Таким образом, мы сохраняем связанные классы вместе. Более того, папки в проекте теперь структурированы по семантике модели предметной области, а не по назначению утилиты. Эта версия относится к первой категории, и настоятельно рекомендуется сохранить такое разделение в своем решении.
Сohesion и SRP
Понятие cohesion похоже на Принцип единой ответственности. SRP утверждает, что класс должен нести единую ответственность (единую причину для изменения), что аналогично тому, что делает код с высоким cohesion.
Разница здесь в том, что, хотя высокий cohesion подразумевает, что код имеет схожие обязанности, но это необязательно означает, что код должен иметь только одну. Можно сказать, что SRP в этом смысле более строгий.
Резюме
Давайте подведем итоги:
Cohesion представляет собой степень, в которой часть кодовой базы образует логически единую атомарную единицу.
Coupling представляет собой степень, в которой один блок независим от других.
Невозможно добиться полного разделения (decoupling) без нарушения целостности (cohesion), и наоборот.
Старайтесь придерживаться принципа «high cohesion и low coupling» на всех уровнях вашей кодовой базы.
Не попадайтесь в ловушку деструктивной развязки.
Теория программирования: пакетные принципы и метрики
Чтобы применять любые принципы правильно, сначала нужно их понять — то есть осознать, откуда они взялись и для чего нужны. Если применять вслепую всё, что угодно — результат будет хуже, чем если бы мы вообще не использовали эти принципы. Я начну издалека и сначала расскажу про абстракцию.
Что есть абстракция?
Это обобщение существенного и удаление несущественного, так как мир настолько сложен, что запрограммировать удаётся только его существенные части. Если попытаемся запрограммировать всё — мы потонем, поэтому абстракция помогает нашему мозгу «впихнуть невпихуемое», как это умеют делают военные (а программисты — пока нет):
Наши проблемы идут от мозга и от того, как устроена наша память. В отличие от хорошего хранилища долговременной памяти, кратковременная устроена по типу stack:
Еще мы используем Chunking (группировку) всякий раз, когда важно запомнить что-то большое. Например, чтобы запомнить число 88003334434, мы разделим его на группы по типу телефонного номера: 8-800-333-44-34. Для нашего мозга получится 5 объектов, которые запомнить легче, чем пытаться удержать число целиком или отдельно каждую его часть.
В коде мы используем слои, чтобы облегчить эту задачу. Однако пять таких слоев в голове удержать становится сложно, а больше пяти — уже проблема. Для лучшего представления это можно сравнить с неправильной супер-лазаньей, в которой слишком много слоёв и — они все слиплись. И это гораздо хуже, чем спагетти — потому что спагетти мы можем отрефакторить и что-то нормальное в результате получить. А чтобы получить что-то нормальное из такой лазаньи, её надо сначала растерзать и превратить в понятные и очевидные спагетти, а потом заново собирать лазанью, только уже правильную.
Поэтому, чтобы совладать с неподъёмно сложными системами, изобретают архитектуру, используя абстракцию. Это не цель, а инструмент, и одновременно — необходимое зло, так как все эти классы, супер-паттерны и прочие шутки не от нашей хорошей жизни.
Но как построить абстракцию, не сделав хуже?
Существует два понятия: cohesion (связность) и coupling (связанность). Они относятся в первую очередь к классам, но в целом и ко всем остальным сущностям. Разницу мало кто видит, так как звучат они почти одинаково.
И, хотя оба означают связь, coupling понимают в негативном ключе. Один объект завязан на другой в плохом смысле, если, ломая один из объектов, ломается всё остальное по цепочке. Cohesion несёт в себе позитивную ноту. Это группировка, в которой то, что близко по смыслу — лежит в одном месте и взаимодействует примерно с теми же местами, опять же близкими по смыслу.
Для того, чтобы понять, coupling у вас или cohesion, существуют проверочные правила. Их сформулировал инженер и специалист в области информатики Роберт Мартин еще в 2000 году, и это — принципы SOLID:
Что есть пакет?
Пакет — это группа единиц кода. Причем, пакеты – это не обязательно пакеты Maven или Composer, или npm. Это программные модули, — то, что вы выделяете в namespaces или иным способом группируете.
Обычно имеются в виду классы, но это могут быть и библиотеки, и модули (не те, которые в фреймфворках, а которые изначально описывали в объектно-ориентированном программировании, то есть группы относящихся друг к другу классов в отдельном namespace). И даже микросервисы можно назвать пакетами — если это настоящие микросервисы, а не те макро-, на которые частенько распиливают монолиты.
И того, кто эти пакеты пилит, обычно волнуют два вопроса: Как правильно формировать пакеты и как работать с зависимостями пакетов?
Как их правильно формировать?
Собственно, cohesion и coupling, как основополагающие принципы, отлично работают и для пакетов. Но сработает ли SOLID для пакетов?
Да, но не совсем. Оказалось, что существуют ещё 6 принципов от того же Роберта Мартина, сформулированные в том же году. Часть из них относится к cohesion, и это о том, как дизайнить код: REP, CCP, CRP. Другая часть — это coupling (то есть использование пакетов): ADP, SDP, SAP — и это о том, как сделать так, чтобы один пакет не завязался на другой и чтобы всё нормально работало:
1 принцип – REP (Reuse-Release Equivalency Principle)
На сегодняшний день этот принцип выглядит до смешного очевидным, но не забываем, что сформулирован он в 2000 году, когда не было таких замечательных штук, как Maven, Composer и прочих, а пакетные релизы были не частыми.
Принцип гласит: «The granule of reuse is the granule of release. Only components that are released through a tracking system can effectively be reused. This granule is the package. — что переиспользуем, то и релизим. Эффективно переиспользовать можно только компоненты, релизнутые через системы версионирования. Такие компоненты называются пакетами». То есть упаковывайте то, что переиспользуется в отдельные пакеты и релизьте это через любимый пакетный менеджер, версионируя по SemVer.
2 принцип – CCP (Common Closure Principle)
«Classes that change together are packaged together — изменение в пакете должно затрагивать весь пакет». Этот принцип очень похож на SOLID-ский OCP. Классы, которые изменяются по одной и той же причине, должны упаковываться в один пакет. Что логично.
Нормальный пример: адаптеры. Библиотека, допустим, кеш. Если мы запихиваем в один пакет тучу адаптеров: для файлов, memcached, Redis, то при попытке изменить один какой-то адаптер мы нарушаем два принципа. Во-первых, принцип REP (начинаем релизить один из адаптеров, а релизить приходится все). А во-вторых — принцип CCP. Это когда классы для адаптера под Redis изменяются, а все остальные адаптеры в пакете —нет.
3 принцип – CRP (Common Reuse Principle)
«Classes that are used together are packaged together — Пакеты должны быть сфокусированными. Использоваться должно всё». То есть классы, которые используются вместе — упаковываем вместе. Проверочное правило здесь такое: смотрим, используется ли в нашем софте всё из того пакета, который к нему подключен. Если используется чуть-чуть, значит, скорее всего, пакет спроектирован неверно.
Эти три принципа дают понимание, как пакеты дизайнить. И казалось бы, нормально делай — нормально будет. Однако реальность сурова, и я сейчас объясню — почему. Вспомним треугольник от Артемия Лебедева, который вершины «быстро», «дёшево» и «качественно» обозначил несколько другими словами. Такой же треугольник нарисовали и для пакетных принципов в Институте Макса Планка:
Получается, эти принципы конфликтуют, и в зависимости от того, какие стороны треугольника мы выбираем, вылезают соответствующие косяки:
Теперь переходим к принципам использования.
4 принцип – ADP (Acyclic Dependencies Principle)
«The dependency graph of packages must have no cycles — Если есть циклические зависимости, то проблема вызывает лавину». Если есть циклы, то есть зависимость пакета зависит от самого пакета прямо или косвенно, то косяк в одном пакете вызывает лавину во всех остальных пакетах, и ломается абсолютно всё. К тому же, такие пакеты очень тяжело релизить.
Поэтому надо проверять, есть ли циклы. Для этого необходимо строить направленный граф зависимостей и смотреть на него. Руками это делать не очень удобно, поэтому для PHP есть библиотека clue/graph-composer, которой скармливаешь пакет, и она строит гигантский граф со всеми зависимостями. Смотреть на это, конечно, невозможно, поэтому надо зайти в PR#45, зачекаутить его и выбрать возможность исключать зависимости, которые не интересны. Допустим, если вы пишите фреймворк, то вам скорее всего интересны зависимости на свои пакеты, а чужие — не так сильно, ведь свои косяки поправить можем, чужие — тяжелее. И получается вот такой граф:
Если мы видим — как здесь — что циклических зависимостей нет, то всё отлично. Если есть, надо исправлять. Чем меньше зависимостей, тем проще.
Как разорвать цикл?
5 принцип – SDP (Stable Dependencies Principle)
Это принцип стабильных зависимостей: «Depend in the direction of stability — Не получится строить стабильное на нестабильном». Нестабильность считается так:
Если на нас завязалось очень много всего — скорее всего, мы стабильны. Если же мы завязались на много всего, то, очевидно, мы не очень стабильны. Как повысить стабильность? Следующим принципом.
6 принцип – SAP (Stable Abstraction Principle)
Принцип стабильных абстракций гласит «A package abstractness should increase with stability — Стабильные пакеты абстрактны / Гибкие конкретны». То есть абстрактность должна возрастать со стабильностью. Стабильность здесь — то, как часто нам приходится менять части пакета: классы, интерфейсы, или что-либо ещё. Абстрактные пакеты должны быть стабильны, чтобы безболезненно на них завязываться. В примере с тем же кэшем пакет с интерфейсом будем сверхстабильным, потому что менять интерфейс, про который мы договорились и хорошо над ним подумали — скорее всего, не придётся. Если мы, конечно, абстрагируем не СУБД.
А вот гибкие пакеты, наоборот, конкретны — они должны быть нестабильны, чтобы мы их легко меняли. Все наши приложения, наши конечные пакеты — это на самом деле дико нестабильная штуковина, которая зависит от тучи всего и ломается чаще, чем пакеты, от которых она зависит.
Можно ли измерить абстрактность?
Конечно. Абстрактность — это число абстрактных классов и интерфейсов в пакете, деленное на общее число классов и интерфейсов в этом самом пакете:
Еще есть такой полезный показатель, как D-метрика, в которой по вертикали — нестабильность, а по горизонтали — абстрактность. По двум зонам — вверху справа и внизу слева — мы можем понять:
Линия посередине называется главной линией, и если классы и интерфейсы попадают на неё или выстраиваются вдоль — это тот случай, когда всё отлично. По сути, D-метрика — это дистанция от главной линии, поэтому 0 в этом случае — это хорошо, а 1 — плохо. Но, как правило, ни то, ни другое не случается — значения плавают в диапазоне от 0 до 0,9-0,7. Считается метрика так:
Для PHP есть 2 инструмента для того, чтобы посмотреть метрику своих пакетов:
Как и SOLID, все эти дополнительные принципы и метрики — не догма, но могут быть весьма полезными.
Резюме
Конечно, есть области, в которых можно сознательно игнорировать все эти принципы, отдавая себе отчёт в том, что за этим стоит и где вы можете получить проблемы.
Данные принципы же позволяют не скатываться в монолит или в left-pad из npm. С left-pad была в свое время история — его создали для добавления символа в конце строки, так как в JavaScript есть традиция дробить пакеты вообще в пыль. А потом на этот пакет завязалось практически всё — вплоть до пакетных менеджеров и самых крутых фреймворков. В какой-то момент автор обиделся на всех и выпилил left-pad из системы — после чего, как вы понимаете, сломалось всё. Рассмотренные принципы, в том числе, позволяют уменьшить вероятность такого сценария.
Единственная конференция по PHP в России PHP Russia 2021 пройдет в Москве 28 июня. Первые доклады уже приняты в программу!
Купить билеты можно тут.
Хотите получить материалы с предыдущей конференции? Подписывайтесь на нашу рассылку.
От STUPID кода к SOLID коду
На прошлой неделе я выступил с докладом об Объектно-ориентированном программировании в Мишлене, в компании, где я работаю. Я рассказывал о написании более эффективного кода, от STUPID коду SOLID коду! STUPID, а также SOLID являются акронимами, и рассматривались довольно много в течение длительного времени. Однако эти мнемоники не всегда известны, таким образом, имеет смысл распространить эту информацию.
Далее я познакомлю вас с принципами STUPID и SOLID. Следует иметь в виду, что это принципы, а не законы. Однако, рассматривая их в качестве законов было бы хорошо для тех, кто хочет усовершенствоваться в написании кода.
STUPID код, серьезно?
Это может задеть Ваше самолюбие, но вы уже, вероятно, написали много STUPID кода. Я тоже. Но, что это значит?
Синглтон
Шаблон «одиночка» — вероятно, самый известный шаблон разработки, а также и самый недооцененный. Вы знаете о синдроме «одиночки»? Это — когда вы думаете, что шаблон «синглтон» — наиболее подходящий шаблон для текущего варианта использования, который у вас есть. Другими словами, вы используете его везде. Это определенно не круто.
Одиночные элементы противоречивы, и их часто считают ошибочными шаблонами. Вы должны избегать их. Фактически, использование синглтона не проблема, а признак проблемы. Вот две причины почему:
Но должны ли вы действительно избегать их все время? Я сказал бы да, потому что часто можно заменить использование синглтона чем-то лучшим. Избежание всего статического очень важно, чтобы не допустить сильной связанности.
Сильная связанность
Сильная связанность — это обобщение проблемы синглтона. Одним словом, вы должны уменьшить связь между своими модулями. Связанность — это мера того, насколько связаны подпрограммы или модули.
Если внесение изменения в одном модуле в вашем приложении требует, чтобы вы изменили другой модуль, тогда связанность существует. Например, вы инстанцируете объекты в классе своего конструктора вместо передачи экземпляров как параметров. Это плохо, потому что он не допускает дальнейших изменений, такие как замена экземпляра экземпляром подкласса, объектом-mock или что бы то ни было.
Сильно связанные модули трудно повторно использовать, и также сложно тестировать.
Невозможность тестирования
По моему мнению, тестирование не должно быть трудным! Нет, правда. Каждый раз, когда вы не пишете unit тесты, потому что у вас нет времени, реальная проблема заключается в том, что ваш код не так уж эффективен, но это — другая история.
В большинстве случаев невозможность тестирования вызвана сильной связанностью.
Преждевременная оптимизация
Дональд Эрвин Кнут сказал: «преждевременная оптимизация — корень всех зол. Только одни затраты, и никакой пользы». Фактически, оптимизированные системы гораздо сложнее, чем просто написание цикла или использование преинкремента вместо постинкремента. В конечном итоге, вы останетесь с нечитабельным кодом. Именно поэтому Преждевременную Оптимизацию часто считают ошибочной.
Мой друг часто говорит, что есть два правила для оптимизации приложения:
Не дескриптивное присвоение имени
Это должно быть, очевидно, но все же нужно это сказать: назовите свои классы, методы, атрибуты и переменные должным образом. Ох, и не сокращайте их! И да, вы пишете код для людей, не для машин. Они не понимают то, что вы пишете, так или иначе. Компьютеры понимают только 0 и 1. Языки программирования предназначены для людей.
Дублирование кода
Дублированный код неэффективен, таким образом, не повторяйтесь, и также делайте его короче и проще. Пишите код только один раз!
Теперь, когда я объяснил, что собой являет код STUPID, вы можете подумать, что ваш код является кодом STUPID. Это (еще) не имеет значения. Не расстраивайтесь, сохраняйте спокойствие и вместо этого напишите SOLID код!
SOLID спешит на помощь
SOLID — термин, описывающий набор принципов разработки для эффективного кода, который был изобретен Робертом К. Мартином, также известным как Uncle Bob.
Принцип единственной обязанности
Принцип единственной обязанности (Single-Responsibility Principle, SRP) гласит, что на каждый класс должна быть возложена одна-единственная обязанность. У класса должна быть только одна причина для изменения.
Просто, потому что вы можете добавить все, что хотите в свой класс, не означает, что вы должны это делать. Обязанности помогут вам разработать приложение лучше. Спросите себя, должна ли логика, которую вы представляете, находиться в этом классе или нет. Использование уровней в приложении очень помогает. Разделите большие классы на меньшие и избегайте “божественных” классов. Последнее, но не менее важное, напишите простые комментарии. Если начинаете писать комментарии такие как in this case, but if, except when, or, то вы делаете это неправильно.
Принцип открытости/закрытости
Принцип открытости/закрытости (Open/Closed Principle, OCP): Сущности (классы, модули, функции и т.п.) должны быть открытыми для расширения, но закрытыми для модификации.
Вы должны сделать все переменные экземпляра private по умолчанию. Пишите методы get и set только, когда они действительно будут вам нужны. Я уже ответил на этот вопрос в предыдущей статье, поскольку девятое правило Объектной Гимнастики связано с этим принципом.
Принцип подстановки Барбары Лисков
Принцип подстановки Лисков (Liskov Substitution Principle, LSP ): Должна быть возможность вместо базового типа подставить любой его подтип.
Давайте рассмотрим пример. Прямоугольник — плоская фигура с четырьмя прямыми углами. У него есть ширина (width) и высота (height).
Теперь, взгляните на следующий псевдо-код:
Мы просто устанавливаем ширину width и высоту height на экземпляре Rectangle, и затем мы подтверждаем, что оба свойства правильны. Пока все идет хорошо.
Теперь мы можем улучшить наше определение, сообщив, что прямоугольник с четырьмя сторонами одинаковой длины называют квадратом. Квадрат — это прямоугольник, таким образом, мы можем создать класс Square, который расширяет класс Rectangle, и заменить первую строку, представленную выше, нижней:
Согласно определению квадрата, его ширина равна его высоте. Вы можете определить проблему? Первое утверждение перестанет работать, потому что мы должны были изменить поведение методов set в классе Square, чтобы соответствовать определению. Это нарушение Принципа подстановки Барбары Лисков.
Принцип разделения интерфейса
Принцип разделения интерфейса (Interface Segregation Principle или ISP): много специализированных интерфейсов лучше, чем один универсальный. Другими словами, вам не придется реализовать методы, которые вы не используете. Осуществление ISP дает слабую связанность и сильную связность.
Когда речь идет о связанности, связность также часто упоминается. Сильная связность означает сохранять подобные и связанные элементы вместе. Объединение связности и связанности является ортогональной структурой.
Идея состоит в том, чтобы сохранить компоненты ориентированными, и попытаться минимизировать зависимости между ними.
Обратите внимание на то, что это подобно Принципу единственной обязанности. Интерфейс — контракт, который удовлетворяет потребности. Нормально иметь класс, который реализует различные интерфейсы, но будьте осторожны, не нарушайте SRP.
Принцип инверсии зависимостей
Принцип инверсии зависимостей (Dependency Inversion Principle или DIP) имеет два основных положения:
Этот принцип можно перефразировать, как использовать тот же уровень абстракции на заданном уровне. Интерфейсы должны зависеть от других интерфейсов. Не добавляйте конкретные классы к сигнатурам методов интерфейса. Однако используйте интерфейсы в своих методах класса.
Обратите внимание на то, что Принцип инверсии зависимостей не совпадает с Внедрением зависимостей. Внедрение зависимости это когда один объект знает о другом зависимом объекте. Иными словами, речь идет о том, как один объект получает зависимость. С другой стороны, Принцип внедрение зависимости заключается в уровне абстракции. Кроме того, контейнер внедрения зависимости — это способ для автоматического соединения классов. Это не означает, что вы делаете внедрение зависимости все же. Например, взгляните на Service Locator.
Также, вместо того, чтобы работать с классами, которые являются сильно связанными, используйте интерфейсы. Это называется программирование интерфейса. Он уменьшает зависимость от особенностей реализации и допускает повторное использование кода. Он также гарантирует, что Вы сможете заменить реализацию, не нарушая ожидания того интерфейса, согласно Принципу подстановки Барбары Лисков.
Заключение
Как вы, наверное, заметили, избежание сильной связанности здесь является ключевым элементом. Здесь находиться большое количество кода, и если вы начинаете, сосредоточив внимание на исправление этого в одиночку, то вы сразу начнете писать лучший код.
Вот вам совет. Существует много принципов в разработке программного обеспечения. Даже если вы не понимаете все эти принципы, всегда думайте перед написанием кода. Не торопитесь, и постарайтесь понять то, что вы не понимаете.
Честно говоря, написание SOLID кода не так уж и сложное занятие.
STUPID — это акроним, который описывает неудачный опыт в Ориентированном Объектном Программировании:
SOLID — это акроним пяти основных принципов объектно-ориентированного программирования и дизайна и проектирования, чтобы исправить STUPID код: