архитектура для различных модулей на одной странице

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

Введение

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

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

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

Архитектура

Страница установит требования в Config в соответствии с желаемыми модулями, включая порядок их отображения. После того, как ViewModel получает конфигурацию, она генерирует связанные задачи и параллельно запрашивает данные. Когда любая задача завершается, она передаст модель пользовательского интерфейса обратно в ViewModel, который отсортирует все полученные модели пользовательского интерфейса в соответствии с настройкой конфигурации, а затем передаст отсортированные модели пользовательского интерфейса в DelegateAdapter. В DalegateAdapter много AdapterDelegates. Каждый AdapterDelegate будет обрабатывать один вид модели пользовательского интерфейса и превращать ее в модуль, который требуется для представления Page.

Страница

Страница обычно отвечает за отображение модулей и обработку событий пользовательского интерфейса. Страница инициализирует Config, ViewModel и DelegationAdapter и регистрирует, какие модули ему нужны, а также порядок модулей в Config и передает Config в ViewModel. Затем страница уведомляет ViewModel о выполнении задач и обновлении пользовательского интерфейса из DelegationAdapter.

Конфиг

Config записывает желаемые модули и порядок отображения. При передаче в ViewModel ViewModel будет создавать задачи для получения данных в соответствии с объявленными модулями в Config.

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

ViewModel

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

ViewModel получает событие пользовательского интерфейса от страницы и выполняет задачи для получения моделей пользовательского интерфейса параллельно. Когда он получает модели пользовательского интерфейса из задачи, коллекция резюмирует их. Наконец, ViewModel передает упорядоченный список моделей пользовательского интерфейса в DelegationAdapter для обновления модулей.

DelegationAdapter

DelegationAdapter - это мост между Page и ViewModel. Он наблюдает за потоком модели пользовательского интерфейса из ViewModel и выводит соответствующие модули на страницу, которая обновляет пользовательский интерфейс. По сути, DelegationAdapter - это диспетчер, который перенаправляет модель пользовательского интерфейса соответствующему AdapterDelegate.

Когда ViewModel выдает упорядоченный список моделей пользовательского интерфейса, DelegationAdapter отправляет каждую модель пользовательского интерфейса в AdapterDelegate. То, с чем будет работать AdapterDelegate, зависит от типа модели пользовательского интерфейса. После этого AdapterDelegate обработает модель пользовательского интерфейса и преобразует ее в модули.

АдаптерДелегат

Пейджу нужны различные модули. Чтобы не быть слишком сложным из-за одного обработчика, мы разделяем сложность на разные AdapterDelegates. Один AdapterDelegate отвечает только за один тип модуля. Он получает модель пользовательского интерфейса, раздувает макет модуля и связывает модель пользовательского интерфейса для обработки представления пользовательского интерфейса модуля.

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

Задача

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

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

2.) Уровень репозитория (или сетевой уровень, например, OKHTTP или AFNetworking) будет кэшировать дублированные данные, чтобы не было дополнительных сетевых подключений при запросе тех же данных.

Модель пользовательского интерфейса

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

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

Преимущество

Простое A / B-тестирование и динамические настройки

Если мы хотим провести A / B-тестирование различных порядков модулей, нам нужно создать только два отдельных объекта Config в зависимости от сегментов. Тогда поток будет отображаться по-разному в разных сегментах.

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

Лучшая производительность

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

Улучшение возможности повторного использования

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

Достижение

На основной целевой странице показаны все модули, включая те, Banner, Channel, Announce, Target и т. Д. другие вспомогательные целевые страницы удаляют Announce, Target и некоторые модули. В нашей архитектуре мы можем отменить регистрацию тех модулей, которые не нужны вспомогательным целевым страницам.

Другой пример: приложение для покупок отображает модуль Рекомендовать на двух разных страницах, и мы можем использовать один и тот же AdapterDelegate, и нам нужно только зарегистрировать этот модуль в конфигурациях двух страниц. Затем на этих страницах появится модуль «Рекомендовать».

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

[1] Адаптер делегата
https://github.com/sockeqwe/AdapterDelegates