Прежде чем перейти к статье, хочу вам представить, экономическую онлайн игру Brave Knights, в которой вы можете играть и зарабатывать. Регистируйтесь, играйте и зарабатывайте!
Геометрическое ядро C3D по своей сути является набором инструментов для создания программного обеспечения (SDK), все его компоненты — геометрический моделер, решатель геометрических ограничений, конвертеры данных, движок визуализации — предлагают программные интерфейсы для использования их функционала в инженерном 3D-приложении (CAD, CAE, CAM и др.). Как и любая другая программа, C3D Toolkit постоянно пополняется новым функционалом, что непосредственно сказывается на API его компонентов. При этом важно сохранять состояние API рабочим и полностью предсказуемым для пользователей.
В этой заметке расскажем о том, что мы учитываем при разработке нового API, как обеспечиваем его обратную совместимость, поддерживаем стабильность и качество.
Особенности разработки API
Прежде чем перейти непосредственно к теме поста, необходимо подробнее остановиться на том, какими особенностями обладает API в принципе.
Общеизвестным является факт, что программные интерфейсы создают разработчики, а используют тоже разработчики, но применяющие их для создания своих собственных программных продуктов. Это позволяет нам утверждать следующее:
API используется для решения различных задач при разных сценариях и получаемых данных;
API используется в разном окружении (операционная система, тип компилятора, версия компилятора);
продукты пользователей API имеют свои собственные жизненные циклы, планы разработки и выпуска релизов.
Учитывая вышеизложенное, программные интерфейсы должны отвечать следующим основным требованиям:
API должен быть простым, интуитивно понятным и согласованным;
API должен поддерживать различные платформы разработки;
API должен обеспечивать стабильность и обратную совместимость;
дизайн API должен предусматривать возможность его развития без нарушения стабильности.
Здесь стоит остановиться подробнее на двух упомянутых выше понятиях – стабильности и совместимости, а именно на том, что они означают в терминах API.
Под стабильностью понимается неизменность программных интерфейсов на протяжении максимально долгого периода времени. При этом если появляется необходимость модификации API, то существует четкая процедура по внесению изменений, направленная, в первую очередь, на поддержание удобства применения API пользователем. Здесь как раз на сцену выходит обратная совместимость, которая означает, что код, написанный с использованием предыдущей версии, функционально одинаково работает и со следующей версией API.
Новый API ядра C3D: принципы разработки
При разработке API геометрического ядра C3D Modeler мы неуклонно следуем следующим важным принципам.
Добавление нового интерфейса никогда не вызывает трудностей, однако удаление уже существующего, ставшего, например, лишним, может стать причиной недовольства пользователей API. Поэтому при удалении такого интерфейса важно проводить целую процедуру, включающую информирование пользователя о необходимости его перехода на новый интерфейс и заменой старого. Соответственно требуется обеспечить минимальный набор интерфейсов, т.е. реализовать только те функции, которые нужны для работы API в данный момент.
Развитие (расширение) программных интерфейсов должно осуществляться таким образом, чтобы не возникало потребности вносить какие-либо изменения в существующий API. При этом расширение интерфейсов не должно пагубно сказываться на их стабильности и нарушать их совместимость с прошлыми версиями API.
Ниже мы расскажем, какими правилами пользуемся при расширении нашего API.
При разработке структуры параметров каждого конкретного интерфейса-функции необходимо избегать длинных списков аргументов, т.к. большое их количество сложнее контролировать, кроме того это может привести к некорректной работе интерфейса. Здесь необходимо выделять смысловые блоки аргументов и упаковывать их в отдельные структуры.
Владение объектами, которые создает интерфейс, должно быть однозначным. Для соблюдения этого принципа хорошей практикой будет использование умных указателей.
Реализация новых интерфейсов по возможности должна сопровождаться использованием уже существующих. Таким образом обеспечивается так называемое «неявное тестирование», когда функция нового API использует другие интерфейсы.
В завершение стоит отметить и важность документации. Любые программные интерфейсы, которые видит пользователь, должны быть хорошо задокументированы, т.к. даже в хорошо спроектированном API внешнему пользователю будет непросто разобраться без подробного описания всех его ключевых моментов.
Новый API ядра C3D: пример разработки
Рассмотрим пример соблюдения упомянутых выше принципов в нашем собственном API геометрического ядра.
Ниже приведены три устаревшие функции для построения тела скругления тремя различными способами.
Пример показывает, что для построения различными способами тела скругления применяются три интерфейса, а также множество аргументов.
Теперь продемонстрируем пример уже нового интерфейса, который пришел на смену трем предыдущим.
Очевидно, что в данной функции все параметры, использовавшиеся в качестве аргументов в предыдущих функциях, инкапсулированы в единую структуру - класс MbShellFilletValues.
Рассмотрим подробнее данный класс.
При его разработке мы следовали некоторым описанным выше принципам. Во-первых, класс предоставляет специальный конструктор для каждого способа построения (принцип 1) и использует умные указатели (принцип 4). Во-вторых, он предоставляет возможность расширения функциональности построения скруглений без нарушения обратной совместимости данного интерфейса (принцип 2). Например, можно добавить новый способ построения, определив конструктор, или расширить функционал существующего интерфейса, добавив иные параметры построения и метод для их инициализации. При этом пользователи могут продолжать использовать этот интерфейс, т.к. для них в плане совместимости ничего не меняется.
Обеспечение стабильности и обратной совместимости API C3D
Итак, мы разобрались, что такое стабильность и обратная совместимость API, подчеркнули важность этих параметров для хорошо спроектированного программного интерфейса, а теперь давайте выясним, каким образом мы в C3D обеспечиваем стабильность и совместимость API.
Необходимо пояснить, что в рамках одной мажорной версии (например, C3D Toolkit 2022):
обновление ревизии API не нарушает компилируемость продуктов пользователей;
обеспечивается стабильность поведения интерфейсов ядра;
массивные изменения API разрешаются только при выпуске следующей мажорной версии;
интерфейс может быть объявлен устаревшим с указанием срока его действия, при этом после указанного срока устаревший API удаляется в любой момент времени (перестает быть стабильным).
Говоря о стабильности и совместимости программных интерфейсов, невозможно не сказать об их развитии и расширении. Мы в C3D придерживаемся следующих правил при изменении API ядра:
интерфейс не может перемещаться или переименовываться без предоставления обратно совместимых псевдонимов;
в интерфейсе-функции не разрешены изменения соглашения вызова, количества параметров, их типа, но в качестве исключения новый параметр может быть добавлен в конец списка параметров существующей функции с обязательным указанием значения по умолчанию (аналогичное правило применяется для методов интерфейсных классов);
в структурах интерфейсов не разрешено удаление, перемещение или изменение публичных полей. Однако есть исключение: при добавлении нового поля в структуру оно может быть добавлено только в конец списка полей, а при добавлении нового значения в перечисление оно также может быть добавлено только в конец списка его значений. Упомянутые правила распространяются на все виды интерфейсов (функции, классы, перечисления, публичные константы);
при необходимости внесения изменений в метод интерфейсного класса в него должен добавляться новый метод, а существующий – объявляться устаревшим, при этом новый метод не должен нарушать или изменять значение и поведение существующего (то же касается и интерфейс-функции). Здесь также существуют исключения, ограничивающие поддержание стабильности и совместимости: обратная совместимость интерфейса может быть нарушена без объявления его устаревшим, если критическая ошибка делает его непригодным для использования, а находящиеся в разработке интерфейсы не обязаны подчиняться требованиям стабильности.
Для удобства пользователей мы выносим информацию об изменениях API в определенных местах:
описываем их в файле changes.txt соответствующего релиза C3D;
отмечаем в комментариях находящиеся в разработке (нестабильные) интерфейсы как экспериментальные;
отмечаем подлежащий удалению интерфейс как устаревший с указанием заменяющего его интерфейса (сроки удаления устаревшего интерфейса указываются в сообщении во время компиляции).
warning C4996: 'FilletSolid':This is a deprecated API that will be removed in version 2022! Use FilletSolid with 'MbShellFilletValues' parameter instead it.
Таким образом, мы стремимся предоставлять нашим пользователем компоненты для разработки инженерных приложений, обладающие интуитивно понятными и удобными программными интерфейсами, в стабильности и совместимости которых пользователи могут быть уверены.