Код на репите. Механизмы повторного использования кода: от элитного до простого

от автора

Привет! Меня зовут Грант, я .Net-разработчик. Знаете вы это или нет, но ваш код можно переиспользовать: будь то модуль, компонент или архитектура. Одни разработчики делают это осознанно, другие на уровне рефлексов. Повторное использование хорошего кода экономит время и другие ресурсы, позволяет применять лучшие практики на проектах, чтобы в итоге эффективнее решать бизнес-задачи.

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

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

Что такое повторное использование ПО

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

Однако в связи с тем, что под повторным использованием ПО чаще всего понимают повторное использование исходного кода, многие воспринимают его как единственный вариант. Мы разделяем эти понятия, и для ясности приведем такое определение:

Повторное использование программного обеспечения — это процесс создания программных систем на основе существующего программного обеспечения, а не создание их с нуля [2].

Классификация механизмов переиспользования кода (Э. Гамма)
Классификация механизмов переиспользования кода (Э. Гамма)

Он выделяет три уровня повторного использования.

  • Фреймворки находятся на самом высоком уровне. Они определяют ключевые абстракции для решения проблемы. 

  • На среднем уровне — шаблоны проектирования. Они меньше и более абстрактны, чем фреймворки.

  • На самом низком уровне располагаются классы: библиотеки классов, контейнеры, объединения классов (например, контейнеры или итераторы).

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

  1. Еще одна классификация, которая представлена «бандой четырех» Классификация механизмов переиспользования кода (Э. Гамма, Р. Хелм, Р. Джонсон, Дж Влиссидес)

    Классификация механизмов переиспользования кода (Э. Гамма, Р. Хелм, Р. Джонсон, Дж Влиссидес)

    Между указанными на рисунке механизмами нет стрелок, указывающих отношение иерархии, так как все они применяются в паттернах проектирования и относятся к уровню, который лежит непосредственно над уровнем программного кода.

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

    Авторская классификация механизмов переиспользования кода
    Авторская классификация механизмов переиспользования кода
    Что не вошло?

    В нее мы не включили такие элементы, как чистые функции, «ленивые» функции, функции высшего порядка и прочие, так как они входят в подпрограммы.

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

    Еще одна классификация

    Можно также упомянуть классификацию принципов переиспользования дизайна Классификация принципов переиспользования дизайна (Р. Ангусвами, У. Б. Фрейкс)

    Классификация принципов переиспользования дизайна (Р. Ангусвами, У. Б. Фрейкс)

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

    Идиомы

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

    В естественных языках идиома — это фраза, которая имеет не буквальное значение. Например, «she has a green thumb» дословно означает, что «у нее зеленый большой палец». А настоящее значение — «она хороший садовод, у нее талант в выращивании растений». Или идиома на русском языке «держать язык за зубами» означает «молчать, хранить тайну».

    В искусственных языках идиома — это шаблон решения задачи путем комбинирования встроенных элементов языка. Поэтому идиома зависит от конкретного языка.

    Приведем несколько примеров идиом для C#.

    1. «When normalizing strings, it is highly recommended that you use ToUpperInvariant instead of ToLowerInvariant because Microsoft has optimized the code for performing uppercase comparisons»

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

    static bool IsAcceptable(int id, int absLimit) =>     SimulateDataFetch(id) is var results      && results.Min() >= -absLimit      && results.Max() <= absLimit;  static int[] SimulateDataFetch(int id) {     var rand = new Random();     return Enumerable                .Range(start: 0, count: 5)                .Select(s => rand.Next(minValue: -10, maxValue: 11))                .ToArray(); }
    1. Вместо большого количества логических операторов “||”

    if (myVar == 2 || myVar == 5 || myVar == 10)     DoSomeOperation();

    используйте массив соответствующих значений и метод расширения Any<TSource> из пространства имен System.Linq:

    var allowedValues = new int[] { 2, 5, 10 }; if (allowedValues.Any(t => myVar == t))     DoSomeOperations();
    1. Вместо множества условных операторов “If” для поиска значения

    var someKey = FindKey(); if (someKey == null)     someKey = FindAlternateKey(); if (someKey == null)     someKey = FindDefaultKey();

    используйте, например, коллекцию делегатов Func<TResult> из пространства имен System и метод расширения FirstOrDefault<TSource> из пространства имен System.Linq:

    var fallback = new List<Func<SomeObject>> {     FindKey(),     FindAlternateKey(),     FindDefaultKey() }; var someKey = fallback.FirstOrDefault(t => t != null);
    1. Управление ресурсами с помощью конструкции using

    using (StreamReader sr = File.OpenText(path)) { content = sr.ReadToEnd(); }
    1. Используйте цепочку операторов null-объединения “??” для множественного сравнения с null:

    string result = value1 ?? value2 ?? value3 ?? String.Empty;

    Или похожий пример, но из веб-разработки:

    string partner = Request.QueryString["GoogleId"] ??                   Request.QueryString["PartnerId"] ??                   Request.QueryString["UserKey"] ??                   string.Empty;
    1. При использовании оператора switch вместо вложенного условия if/else в выражении case 

    switch (myVar) { case aCase: if (someCondition) { DoX(); } else { DoY(); } break; }

    используйте выражение case/when:

    switch (myVar) { case aCase when someCondition: DoX(); break;  case aCase: DoY(); break; }
    Полезные ссылки

    Эти и некоторые другие идиомы C# можно найти здесь:

    Почти 100 идиом на языке C++ приведено на Wikibooks. И еще множество идиом с фильтрацией по различным  языкам программирования.

    Идиомы существуют также в тестировании. Например, для того, чтобы обеспечить  соблюдение лучших практик при написании тестов с применением Autofixture разработан пакет AutoFixture.Idioms.

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

    Однако при всех своих преимуществах у идиом есть и недостатки. Отметим следующие:

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

    • Лаконичные идиомы не всегда являются эффективным решением.

    Расширенная классификация паттернов

    Следующие механизмы повторного использования, которые мы рассмотрим — это паттерны.

    Обычно имеют в виду 23 паттерна проектирования, которые описаны «бандой четырех» Расширенная классификация паттернов (Д. Кнут «Искусство программирования»)

    Расширенная классификация паттернов (Д. Кнут «Искусство программирования»)

    Про идиомы мы уже говорили ранее, рассмотрим остальные элементы.

    Паттерны реализации (Implementation patterns) — совокупность компонентов и других элементов реализации, используемых в структуре модели при написании программного кода.

    Они делятся на:

    • паттерны организации программного кода;

    • паттерны оптимизации программного кода;

    • паттерны устойчивости кода;

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

    Паттерны тестирования (Test patterns) — особый тип шаблонов, которые могут использоваться на этапе тестирования для уменьшения избыточности, экономии времени и ресурсов и обеспечения эффективного механизма повторного использования для большего покрытия и повышения качества сопровождения одновременно MVC;

  2. HMVC (Hierarchical MVC) — расширение MVC с помощью иерархии триад;

  3. MVP — модификация MVC, в которой View больше не следит за обновлениями Model, события проходят через Presenter;

  4. MVVM — модификация MVC с применением концепции «связывания данных».

  5. EBI (Entity-Boundary-Interactor) — альтернатива MVC, при которой основные элементы и поведения, представляемые объектами Interactor и Entity, отделены от входящих и исходящих данных с помощью Boundary.

  6. Что еще можно отнести к архитектурным паттернам

    В некотором смысле к архитектурным паттернам можно отнести каталог паттернов для архитектуры корпоративных приложений, которые описаны у Мартина Фаулера

    Что не вошло?

    Это был далеко не полный перечень, а всего лишь несколько примеров. Отмечу, что в данный пункт мы специально не включили паттерны GRASP (General Responsibility Assignment Software Pattern) — общие шаблоны распределения ответственностей Рассмотрим классификацию фреймворков, которая учитывает сразу три параметра:

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

    2. По оси ордината отложено два параметра, один из них — Скорость разработки. Тут название говорит само за себя.

    3. Второй параметра по оси ординат — это Управляемость и гибкость, он показывает, какие ограничения накладывает фреймворк.

    Классификация фреймворков
    Классификация фреймворков

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

    Подход “Narrow-focused Frameworks” — это узкоспециализированные фреймворки, которые помогают писать нам корпоративные приложения. Сюда можно отнести как отдельные ORM фреймворки (EF, Hibernate), так и «фреймворки фреймворков» (Spring или ASP.NET Core). Данный подход обладает все еще высокой гибкостью и достаточно большим диапазоном применения.

    Подход “Code Generators/Aggregators” позволяет ускорить разработку на стадии старта проекта. Такие генераторы выстраивают структуру приложений, основанных на узкоспециализированных фреймворках. Например, JHipster генерирует приложение на базе Spring с пользовательским интерфейсом на Angular.

    Подход “Full-stack Frameworks” — фреймворки, которые предоставляют готовую инфраструктуру и позволяют создавать такие приложения как ERP, BPMS. Один из представителей такого класса систем это Jmix.

    Подход “Low/No Code Frameworks” — это самый быстрый способ создания приложений, но и наименее гибкий, потому что в таких фреймворках вы не контролируете кодовую базу, а можете реализовывать только отдельные кусочки логики. Типичные представители данной группы это конструкторы сайтов, CMS системы (WordPress, Drupal и другие).

    Архитектуры

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

    Иоганн Вольфганг Гете писал: «Архитектура — это онемевшая музыка». Действительно, музыка существует в динамике, и точно так же приложение является процессом. У него есть момент запуска, развития и завершения. Архитектура приложения — его срез в любой конкретный момент времени. В любой момент наше приложение как музыкальная тема должно быть четким, ясным, понятным, предсказуемым.

    Часто именно работа с архитектурой – это первый шаг к долгосрочному сотрудничеству с заказчиком. Для команды это возможность показать экспертизу, обеспечить доверие и подключить специалистов к дальнейшей разработке. Я и мои коллеги в SimbirSoft занимаемся этими вопросами в Архитектурном комитете. Это группа экспертов, которые прорабатывают и проводят аудит IT-решений, помогают выбрать оптимальный технологический стек для каждого проекта. За несколько лет мы спроектировали уже более 70 крутых продуктов. 

    Зачем вообще нам нужна архитектура?

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

    • Помогает оценить потребность в ресурсах области решения.

    • Помогает обнаружить в системе области, подверженные риску на этапе архитектуры.

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

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

    В начале статьи мы упоминали понятия в сфере проектирования архитектуры, раскроем их:

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

    Каталоги эталонных архитектур можно найти здесь:

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

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

    В таблице ниже приведен перечень архитектурных стилей, которые соответствуют определенным типам приложений:

    Тип приложения

    Архитектурный стиль

    Разделяемая память

    Классная доска

    Дата-центрический

    Основанный на правилах

    Распределенная система

    Клиент-серверный

    Пространственно-ориентированная архитектура

    Одноранговая

    Архитектура без разделения ресурсов

    Посредник

    Передача репрезентативного состояния (REST)

    Сервис-ориентированный

    Обмен сообщениями

    Событийная

    Асинхронный обмен сообщениями

    Издатель-подписчик

    Структурные

    Основанный на компонентах

    Трубы и фильтры

    Монолит

    Многослойная

    Настраиваемые системы

    Плагины

    Основанный на рефлексиях

    Микроядерный

    Современные системы

    Архитектура распределенных вычисления

    Мультитенантный (мультиарендный) стиль

    Архитектура больших данных

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

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

    Архитектурные шаблоны решают проблемы, связанные с архитектурными стилями. Например:

    • Сколько будет уровней в нашей клиент-серверной архитектур: 1-tier, 2-tier, 3-tier или N-tier?

    • Какие модули стоит выделить в нашей сервис-ориентированной архитектуре?

    На элементы шаблона архитектуры влияют выбранные тактики проектирования, реализующие особенности проектирования и зависящие от них.

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

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

    Еще один важный паттерн, который можно переиспользовать — это шаблон развертывания.

    Об автоматизации процесса развертывания

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

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

    В качестве примеров:
    Еще один пример:

    В заключение темы архитектуры отметим интересный пример под названием Web Architecture 101, который содержит базовые концепции веб-архитектуры и может быть полезен для веб-разработчиков.

    На рисунке представлены основные ее компоненты:

    (1) DNS — Система доменных имен;

    (2) Балансировщик нагрузки;

    (3) Серверы веб-приложений;

    (4) Сервера баз данных;

    (5) Служба кэширования;

    (6a) Очереди и (6b) сервера заданий;

    (7) Служба полнотекстового поиска;

    (8) Сервисы;

    (9a) Data firehose — потоковый интерфейс для приема и обработки данных;

    (10) Облачное хранилище (cloud storage);

    (9c) Хранилище данных (data warehouse);

    (11) CDN (Сеть доставки содержимого).

    Итак, пора ответить на два ключевых вопроса: когда возможен code reuse и какие проблемы при этом возникают?

    Когда стоит использовать code reuse?

    Вы можете повторно использовать код, когда он:

    • легко расширяется и адаптируется под новое приложение;

    • при необходимости портируется на другое оборудование;

    • не содержит дефектов или проблем, которые влияют на надежность, сохранность и защищенность нового приложения;

    • позволяет переиспользовать свою часть, если такое требуется.

    Сложности и проблемы, недостатки и преимущества

    Организация и масштаб

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

    Администрирование

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

    Офисная политика и психология

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

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

    Дополнительное время

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

    Дополнительные ресурсы

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

    Технические проблемы

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

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

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

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

    • Поддерживаемость. Один из способов обеспечить удобство сопровождения кода — обеспечить его соответствие.

    Исходя из вышесказанного, можно выделить такие плюсы и минусы:

    • Сокращение общего времени разработки, так как программистам не нужно начинать все с нуля.

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

    • При code reuse мы получаем единый источник правды, который предоставляет достоверные, актуальные и пригодные к использованию данные.

    • Меньше кода — следовательно, меньше вероятность наличия в нем недостатков, проще понимать код.

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

    ➖ 

    • Сторонняя зависимость может быть менее надежной, чем недавно написанный код, соответствующий стандартам безопасности.

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

    • Если при переиспользовании осуществляется доступ к API по всей системе, то это может снизить скорость работы приложения.

    • Модульная система может оказать негативное влияние на скорость работы системы.

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

    • Могут возникнуть проблемы с лицензированием.

    Заключение

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

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

    Множество полезных ссылок для еще более глубокого погружения в тему:

    1. https://slideplayer.com/slide/8014475/

    2. https://infocom.spbstu.ru/userfiles/files/articles/2019/2/28-38.pdf

    3. Эталонные решения (reference architectures) 

    https://learn.microsoft.com/ru-ru/azure/architecture/serverless-quest/reference-architectures

    https://aws.amazon.com/ru/architecture/?cards-all.sort-by=item.additionalFields.sortDate&cards-all.sort-order=desc&awsf.content-type=*all&awsf.methodology=*all&awsf.tech-category=*all&awsf.industries=*all

    https://learn.microsoft.com/ru-ru/azure/architecture/browse/

    https://docs.gitlab.com/ee/administration/reference_architectures/

    https://www.bptrends.com/reference-architecture-frameworks-a-consolidation/

    https://cloud.google.com/solutions

    https://www.informatica.com/enterprise-architecture.html

    1. Архитектурные шаблоны

    https://habr.com/ru/company/alconost/blog/522662/

    https://itnan.ru/post.php?c=1&p=699648

    https://medium.com/nuances-of-programming/краткий-обзор-10-популярных-архитектурных-шаблонов-приложений-81647be5c46f

    1. Шаблоны развертывания

    https://learn.microsoft.com/ru-ru/azure/azure-resource-manager/templates/deploy-cli

    https://learn.microsoft.com/ru-ru/azure/azure-resource-manager/templates/overview

    https://github.com/MicrosoftDocs/azure-docs.ru-ru/blob/master/articles/azure-resource-manager/templates/overview.md

    https://aws.amazon.com/ru/cloudformation/resources/templates/

    1. Тактики:

    http://architectvelichko.com/blog/architecttactics/

    https://www.osp.ru/os/2015/03/13046898

    https://habr.com/ru/company/iticapital/blog/302990/

    https://pdfs.semanticscholar.org/4296/c1e1d255e674579046e5ad619f6e64015234.pdf

    https://www.researchgate.net/publication/261705779_A_Catalogue_of_Green_Architectural_Tactics_for_the_Cloud

    https://continuousarchitecture.com/2021/06/11/architectural-tactics/

    https://core.ac.uk/download/pdf/43408512.pdf

    Спасибо за внимание!

    Полезные материалы о разработке и архитектуре мы также публикуем в наших соцсетях – ВКонтакте и Telegram.


ссылка на оригинал статьи https://habr.com/ru/company/simbirsoft/blog/716754/


Комментарии

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

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