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

Моя цель - предложение широкого ассортимента товаров и услуг на постоянно высоком качестве обслуживания по самым выгодным ценам.

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

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

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

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

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

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

Повторное использование программного обеспечения — это процесс создания программных систем на основе существующего программного обеспечения, а не создание их с нуля Johannes Sametinger, Software Engineering with Reusable Components. Springer 1997, ISBN 978-3-540-62695-4, pp. I-XVI, 1-272 </p>" data-abbr="[1]">[1].

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

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

  • уровень кода;

  • уровень, лежащий непосредственно над уровнем программного кода (программные структуры, из которых состоят модули);

  • модули;

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

  • архитектуры.

Три классификации механизмов повторного использования кода

  1. Начнем с классификации, которую предлагает Эрих Гамма [2].

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

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

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

  1. Еще одна классификация, которая представлена «бандой четырех» [3]:

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

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

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

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

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

Блок «Архитектуры» многогранен, и множество концептов онтологии проектирования архитектуры может включать в себя:

Более подробно об архитектурах поговорим в одноименном разделе далее.

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

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

Идиомы

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

В естественных языках идиома — это фраза, которая имеет не буквальное значение. Например, «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;
}
Полезные ссылки

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

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

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

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

Обычно имеют в виду 23 паттерна проектирования, которые описаны «бандой четырех» [3].

На следующем рисунке мы привели расширенную классификацию, которая кроме классических паттернов проектирования содержит:

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

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

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

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

Паттерны тестирования (Test patterns) — особый тип шаблонов, которые могут использоваться на этапе тестирования для уменьшения избыточности, экономии времени и ресурсов и обеспечения эффективного механизма повторного использования для большего покрытия и повышения качества сопровождения одновременно Bouchaib Falah, Testing Patterns in Action: Designing a Test-Pattern-Based Suite, May 2015 International Review on Computers and Software (IRECOS) 10(5):489 </p>" data-abbr="[8]">[8].

Паттерны анализа (Analysis patterns) — это группа связанных общих объектов (мета-классов) со стереотипными атрибутами (определениями данных), поведением (сигнатуры методов) и ожидаемыми взаимодействиями, определенными независимо от предметной области [9].

К ним относятся паттерны бизнес-моделирования ARIS (Architecture of Integrated Information Systems). Также каталог таких паттернов представлен в труде Фаулера [10].

Паттерны параллельного программирования (Concurrency and Parallel programming patterns). В настоящее время сложно представить эффективную многопользовательскую программу без конкурентной разработки. Такие паттерны содержат модели успешных решений тех проблем, с которыми разработчики сталкиваются при конкурентной разработке.

При этом полезно отличать понятия конкурентности и ее разновидностей:

Архитектурные паттерны (Architectural patterns) — множество предварительно определенных подсистем со спецификацией их ответственности, правил и базовых принципов установления отношений между ними.

К данной категории можно отнести такие паттерны:

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

Непосредственно паттерны проектирования активно представлены в платформах разработки. Обычно это происходит в двух формах:

  1. Паттерны применены для решения определенных задач в самой платформе.

  2. Платформа предоставляет нужные механизмы (обычно это соответствующие интерфейсы) для удобной реализации паттернов.

Рассмотрим несколько таких примеров на C#.

Примеры используемых паттернов в платформе:

  1. Фабрики/фабричные методы

  2. Строитель, builder

  3. Посетитель

Интерфейсы для реализации паттернов:

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

  2. Для реализации Итератора можно использовать два интерфейса: System.Collections.IEnumerable и System.Collections.IEnumerator. Так же есть их обобщенные версии: System.Collections.Generic.IEnumerable<T> System.Collections.Generic.IEnumerator<T>.

  3. Наблюдатель представлен интерфейсами System.IObservable<T> и System.IObserver<T>. Для любителей реактивных расширений можно вспомнить про System.Reactive.Subjects.Subject<T>.

Что не вошло?

Библиотеки и фреймворки

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

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

Фреймворки состоят из:

Таким примером фреймворков может служить ASP.Net Core. Рассмотрим в нем три примера механизмов, которые содержат неизменяемую и изменяемую части.

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

  2. Реализация шаблона MVC. В нем мы должны создавать контроллеры, имена которых содержат суффикс Controller, или базовый класс контроллера содержит суффикс, или к классу применяется атрибут [Controller]), а файлы представлений, как правило, объединяются в папки с именами соответствующими контроллерам (неизменяемая часть). При таких заданных правилах мы можем создавать произвольные контроллеры и произвольные представления (изменяемая часть).

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

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

Рассмотрим классификацию фреймворков, которая учитывает сразу три параметра:

  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)

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

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

Событийная

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

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

Структурные

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

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

Монолит

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

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

Плагины

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

➕

➖ 

Заключение

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

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

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

  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/


Интересные статьи

Интересные статьи

Андрей Маслов успел поработать и на постройке россиянами военного завода в Венесуэле, и статистом для фильмов Голливуда, а в итоге стал нашим project-менеджером из Мексик...
Я давно знаком с Битрикс24, ещё дольше с 1С-Битрикс и, конечно же, неоднократно имел дела с интернет-магазинами которые работают на нём. Да, конечно это дорого, долго, местами неуклюже...
Сегодня публикуем вторую часть перевода материала, посвящённого статическому анализу больших объёмов серверного Python-кода в Instagram. → Первая часть
Открытие деления ядер в 1930-х повлекло за собой первую угрозу ядерного уничтожения посредством ядерного оружия в 1940-х, за которой последовало обещание чистой и обильной энергии в 1950-х бл...
Один из самых острых вопросов при разработке на Битрикс - это миграции базы данных. Какие же способы облегчить эту задачу есть на данный момент?