Принципы компонентов: Концепция, лежащая в основе кода

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

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

У компонентов есть несколько принципов, далее мы рассмотрим принципы когезии и сцепления.

Сплоченность компонентов

Решение о том, какие классы принадлежат каким компонентам, является важным решением, и оно использует принципы программной инженерии (MARTIN, 2017).

Принципы объединения компонентов представлены следующим образом:

REP — Reuse/Release Equivalence Principle (или Reuse/Release Equivalence Principle на португальском языке).
CCP — общий принцип закрытия
CRP — общий принцип повторного использования

Ниже будет подробно проанализирована каждая из этих трех категорий.

REP: Принцип эквивалентности повторного использования/освобождения

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

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

CCP: Общий принцип закрытия

Принцип общего закрытия гласит, что классы, которые изменяются по одинаковым причинам и в одно и то же время, должны быть собраны в компоненты, а классы, которые изменяются в разное время и по разным причинам, должны быть разделены на разные компоненты (MARTIN, 2017).

Этот принцип представляет собой переформулировку принципа единой ответственности (SRP), рассмотренного в статье о SOLID, с применением его к компонентам. Так же как SRP утверждает, что класс не должен иметь много причин для изменения, CCP утверждает, что компонент не должен иметь много причин для изменения.

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

CCP также связан с другим принципом SOLID, принципом открытости/закрытости (OCP) — также рассматриваемым в статье о SOLID — причем термин «закрытый» используется в обоих случаях с одинаковым значением. В OCP говорится, что классы должны быть закрыты для модификаций, но открыты для расширений. Поскольку полная закрытость невозможна, классы разрабатываются таким образом, чтобы они были закрыты для большинства ожидаемых или наблюдаемых типов изменений. CCP развивает это определение, собирая в один компонент классы, которые закрыты для одинаковых типов изменений, так что, когда происходит изменение требований, оно имеет высокие шансы быть ограниченным уменьшенным количеством компонентов.

CRP: Общий принцип повторного использования

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

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

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

Диаграмма напряжения для сцепления компонентов

Как видно из предыдущих тем, эти три принципа существенно различаются. REP и CCP имеют тенденцию к увеличению количества компонентов, а CRP — к их уменьшению. О взаимодействии принципов друг с другом МАРТИН (2017) представляет диаграмму натяжения, в которой ее ребра описывают стоимость отказа от принципа из противоположной вершины.

Архитектор программного обеспечения должен найти позицию в треугольнике напряженности на рисунке выше, чтобы удовлетворить текущие требования разработки и знать, что эти требования будут меняться с течением времени. Сосредоточив внимание на REP и CRP, можно понять, что внесение простых изменений влияет на многие компоненты. Сосредоточив больше внимания на CCP и REP, это приведет к появлению множества ненужных релизов. MARTIN (2017) утверждает, что обычно проекты начинаются с правой стороны треугольника, а по мере созревания переходят на левую сторону.

Соединение компонентов

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

ADP — принцип ациклических зависимостей (или принцип ациклических зависимостей на португальском языке).
SDP — принцип стабильной зависимости
SAP — принцип стабильных абстракций

Далее каждый из них будет проанализирован.

ADP: принцип ациклических зависимостей

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

Чтобы этого не произошло, необходимо разделить среду разработки на выпускаемые компоненты (MARTIN, 2017). Таким образом, каждый компонент становится рабочей средой под ответственностью разработчика или команды. Если заставить этот компонент работать, создается версия для использования другими командами, после чего разработчики возвращаются к работе в своих личных кабинетах, пока у них не появится другая версия. Остальные команды могут решить, начать ли использовать новый релиз или продолжать использовать старый. Таким образом, изменения в компоненте не обязательно должны иметь немедленный эффект для других команд, каждая команда сама решает, когда адаптировать свои компоненты к новому релизу.

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

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

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

Есть два способа разорвать эти циклы (MARTIN, 2017). Первый — это применение принципа инверсии зависимостей, при котором можно создать интерфейс с методами, необходимыми определенному классу, а затем унаследовать его в другом классе, чтобы осуществить инверсию зависимостей и тем самым разорвать цикл. Второй способ — создать компонент, от которого зависят оба класса, образующие цикл, и перенести существенные классы в обоих компонентах в этот новый компонент. Таким образом, классы становятся зависимыми от этого нового компонента, и цикл разрывается.

SDP: Принцип стабильных зависимостей

Принцип стабильных зависимостей утверждает, что проекты не могут быть полностью статичными, что для поддержания проекта необходима некоторая изменчивость. С помощью принципа общего замыкания создаются компоненты, которые чувствительны к одним изменениям, но не к другим. Некоторые из этих компонентов созданы непостоянными, поэтому ожидается, что они будут меняться (MARTIN, 2017).

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

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

SAP: Принцип стабильных абстракций

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

Заключение

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

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

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

Ссылки

  • MARTIN, Robert C. Clean Architecture: A Craftsman’s Guide to Software Structure and Design. 1-е изд. США: Prentice Hall Press, 2017. ISBN 0134494164.

Оцените статью
Procodings.ru
Добавить комментарий