Более простые и безопасные рабочие процессы для создания объектов и многое другое

Мартин Фаулер, который ввел термин Fluent Interfaces 15 лет назад, написал в своей книге Рефакторинг: улучшение дизайна существующего кода:

«Любой дурак может написать код, понятный компьютеру. Хорошие программисты пишут код, понятный людям ».

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

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

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

Все примеры кода будут на Java или ссылаться на платформы Java. Но общие принципы свободного интерфейса применимы и к любому другому языку.

Свободные интерфейсы

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

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

В объектно-ориентированном программировании цепочка методов - это способ последовательного вызова нескольких вызовов методов. Это позволяет нам писать наш код плавно и более простым способом и уменьшать визуальный шум.

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

Есть три способа сделать код более плавным.

Изменчивый путь

Каждый установщик или связанный метод экземпляра изменяет сам экземпляр и возвращает this.

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

Неизменный путь

Вместо возврата самого экземпляра возвращается новый экземпляр, представляющий измененное состояние. Ярким примером такого поведения является java.time API или java.math.BigDecimal:

С застройщиком-посредником

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

В Java мы можем использовать построители, чтобы обойти отсутствие именованных параметров.

Как создать свободный конструктор

Начнем с типа, описывающего разработчика:

Простой POJO, просто хранящий некоторые данные, без каких-либо дополнительных функций.

Создание простого конструктора

Чтобы улучшить создание экземпляра, нам понадобится дополнительный тип построителя:

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

Использование конструктора

Свободный конструктор разделяет код для построения и представления.

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

Но мы можем еще больше улучшить наш конструктор, а также соответствующий тип.

Улучшение конструктора с неизменяемостью

Часто используемый дизайн - сделать построитель вложенным классом соответствующего типа. Это также означает отказ от чистого POJO, но вместо этого получение неизменяемого типа, что является отличной сделкой на мой взгляд:

Тип Developer теперь полностью неизменяем и может быть только создан с помощью построителя.

Неизменяемое поколение строителей

Давайте будем честными ... это все еще много дополнительного кода. И нам нужно было бы написать его для каждого типа, подобного POJO.

Вместо того, чтобы делать это сами, мы можем использовать такие фреймворки, как Immutables:

Благодаря обработке аннотаций наши компоновщики создаются для использования, включая проверку, больше методов компоновщика, простое копирование и намного больше функций, чем мы когда-либо могли бы сделать сами:

Свободные интерфейсы для рабочих процессов

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

Отличным примером свободного проектирования API является Java Streams API. Мы можем снизить уровень многословности путем объединения вызовов, связанных с потоком, вместе вместо использования традиционных средств итерации по коллекции:

Много кода для попадания в нашу коллекцию имен первых пяти авторов-фантастов 2020 года.

Давайте сделаем этот звонок свободным с потоками:

Если мы включим форматирование и скобки, нам удалось сократить 20 строк кода до ~ десяти строк.

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

Заключение

Свободные интерфейсы удобны для употребления, но не для создания.

Создание API, более похожего на DSL, помогает разработчикам использовать его с менее подробным и более понятным кодом.

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

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

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

Отладка также может быть сложнее, если наша IDE не поддерживает точки останова в разных точках цепочки. Часто это можно преодолеть, разбив цепочку на несколько строк, что по совпадению также улучшает читаемость.

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

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

Ресурсы