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

Некоторые определения

Чтобы понять, что я имею в виду под UI, UX и Business Logic, вот моя интерпретация этих компонентов:

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

Чтобы удовлетворить вышеуказанное бизнес-правило, необходимо предоставить поле имени пользователя и пароля и кнопку отправки (UI). Поскольку пользователь должен иметь возможность взаимодействовать с этими элементами UI (чтобы удовлетворить бизнес правилу), рождается UX. В некотором смысле бизнес правило порождает UI, а UX находится где-то посередине.

Вероятно, легче увидеть различие между логикой UI и Business, но не UX. UX может показаться вам немного похожим на бизнес-логику. Но не волнуйтесь, если вы не понимаете или даже не согласны. Примеры в этой статье говорят сами за себя.

Замечание о примерах: Для этой статьи у меня будет 2 файла: «Интерактор», который должен содержать бизнес-логику, и «представление», которое должно содержать логику пользовательского интерфейса. Это название заимствовано из паттерна проектирования «Гадюка».

Я также буду использовать swift в качестве языка программирования. Если вы разработчик iOS (как и я), возможно, вы немного лучше знакомы с синтаксисом. но вам не нужно быть разработчиком iOS, чтобы следовать этим примерам. В моем коде вы увидите примеры необязательных (?), Объединяемых (??) и «слабых» ссылок. Но вам действительно не нужно понимать, почему они там. Я добавил их, чтобы кто-то не придирчивал к моим примерам и не сказал мне, что я допустил ошибку компиляции или что у меня есть циклы сохранения. Так что думайте об этих примерах как о псевдокоде больше, чем о чем-либо.

Плохой подход: UX смешанный с бизнес-логикой

Во-первых, давайте рассмотрим пример, в котором UX и бизнес-логика смешаны вместе:

Чтобы помочь вам следовать коду, начните с метода EnterUsername, затем следуйте ему в LoginInteractor и снова в LoginView.

Мы быстро замечаем циклический поток между View и Interactor: при каждом изменении UI View должен информировать Interactor, а Interactor затем должен информировать View, чтобы обновить еще один из его компонентов (submit кнопка). В таких простых случаях, как этот, довольно легко проследить этот процесс. Но на более крупных модулях с гораздо большим объемом кода и сложностью это становится труднее. Это особенно верно, если мы переместим эти два компонента в отдельные файлы.

Мы также можем видеть, что не только наш UX смешан с нашей бизнес-логикой, Interactor содержит некоторые ссылки на элементы UI, ссылаясь на «кнопку отправки». Этот компонент должен иметь только бизнес-логику, но по имени он ссылается на конкретный компонент UI.

Когда-то я считал, что решением этой проблемы было использование более абстрактных имен, таких как переименование метода в «allowSubmission ()». Таким образом, Interactor не нужно знать ничего, связанного с пользовательским интерфейсом. Но это также делает наш код менее прямым и трудным для понимания. Мы решили одну проблему, но создали другую.

(спойлер) Если вы когда-нибудь читали «Blink» Малкольма Гладуэлла, вы, возможно, помните главу о Korean Air, в которой Гладуэлл высказал предположение, что плохой рейтинг сбоев Кореи во многом объясняется недопониманием. Младшие пилоты будут общаться со своими капитанами с предложениями, а не с прямыми заявлениями. Во многом это было связано с культурным наследием, которое не позволяло им подвергать сомнению решения старших руководителей. Не раньше, чем они смогли определить это как причину своей проблемы, они не смогли улучшить свой рейтинг. Это аналогичный случай (но с меньшим количеством смертей), что и выше. Но даже если мы не летаем на самолетах, если вы создадите методы, которые намекают, ваше приложение в конечном итоге выйдет из строя.

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

В общем, эти два компонента слишком тесно связаны, есть повторяющиеся состояния, и логика распределена между ними обоими. Это no-bueno и приведет к логическим ошибкам или сбоям!

Хороший подход: UX смешанный с логикой UI

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

Первое, что мы можем заметить, это то, что наш Interactor намного проще и короче. Но не стало ли View больше? Напротив, он тоже стал немного меньше. Нам удалось сохранить строки, потому что мы удалили ненужную логику интерфейса между View и Interactor. В реальном примере VIPER у вас были бы контракты и имитирующие взаимодействия (для тестирования). Так что у нас было бы еще больше бесполезного кода. Методы интерфейса будут всегда, но они удаляют ненужные.

Но кроме того, что код стал короче (что само по себе круто), легче ли ему следовать? Давайте посмотрим.

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

Во-вторых, поток UX находится в одном месте (внутри представления). Так что гораздо проще увидеть, что происходит, с точки зрения UX. Вы можете легко увидеть, что кнопка отправки включается и отключается, когда пользователь взаимодействует с полями имени пользователя и пароля. Нам не нужно следить за спагетти в нескольких файлах, чтобы увидеть, что происходит с кнопкой отправки.

Наконец, в Interactor нет ни одной ссылки на элементы представления. Мы удалили все ссылки, связанные с UI, не абстрагируя методы.

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

Решили 3 задачи. Ввели ли мы какие-нибудь новые? Что ж, это несколько субъективный вопрос, но я бы сказал, что нет. Код короче, мы удалили циклическую зависимость и удалили ссылки на UIUX) в Interactor. Мы также получили некоторую бонусную поддержку компилятора. Это сценарий победы, победы и победы (и победы). 😁

Если мы подумаем об этом более логично, пользователь не может отправить данные, потому что недостаточно информации для отправки. Косвенно Interactor сообщает представлению, что «эй, мне нужно имя пользователя и пароль, если вы хотите, чтобы я что-то делал», используя свой интерфейс. Таким образом, представление сообщает пользователю: «Эй, мне нужны имя пользователя и пароль, прежде чем вы сможете отправить», отключив кнопку отправки.

Когда View получает от пользователя всю информацию, необходимую Interactor, он активирует кнопку отправки. И когда пользователь нажимает кнопку отправки, View теперь может указать Interactor что-то сделать со всей этой информацией. За этим легко следить, все чисто, и все знают, что происходит.

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

Представьте, что View - это вы, а Interactor - ваш менеджер. Если ваш руководитель подходит к вам с вопросом, а у вас нет ответа, но вам нужно спросить, вы можете выполнить такой процесс, как этот:

  1. Сначала вы попытаетесь понять вопрос и то, что ожидается
  2. Вы собираете информацию из других источников, таких как другие люди, ваш код или документация.
  3. Вы сообщите менеджеру о своем ответе только после того, как соберете всю необходимую информацию.

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

Мы видим, что та же логика применяется в коде:

  1. Interactor информирует представление о новом событии (например, о загрузке данных).
  2. В представлении отображаются данные и собираются данные, введенные пользователем. Он следит за тем, чтобы у него было все, что нужно передать взаимодействующему заранее.
  3. Когда, наконец, когда вся информация становится доступной, он сообщает Interactor всю информацию, которая ему нужна.

В любой момент Interactor может вернуться к просмотру и сказать ему: «Привет, дружище, введенные вами данные недействительны!» на основе ответа или какой-либо другой логики, например, более сложных правил проверки. Затем представление может также отображать это для пользователя, делая поля красными, отображая предупреждения, независимо от требований правил UX.

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

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

  • Всегда обновляйте пользовательский интерфейс, прежде чем сообщать Interactor. Допустим, у вас есть функция «Мне нравится», которая чем-то похожа на функцию «Мне нравится» в facebook. Когда пользователь нажимает на нее, всегда сначала обновляйте пользовательский интерфейс, прежде чем сообщать интерактору.
  • Если у вас есть форма, соберите все необходимые данные в представлении, прежде чем передавать их интерактору.
  • Если вы получаете данные с сервера, всегда ждите инструкций от интерактора, прежде чем показывать их. Вы можете использовать индикаторы загрузки или каркасные представления, чтобы показать пользователю, что вы ждете инструкций.
  • Рассматривайте индикаторы загрузки как способ указать, что вы ждете ввода от Interactor. Это означает, что вы должны включить загрузку индикаторов из представления, а не из Interactor'а, если вам нужно дождаться ответа.
  • Разрешить Interactor сообщать представление о конкретных инструкциях, вытекающих из бизнес-правил. Например, если пользователю не разрешено редактировать свое имя пользователя из-за разрешения, пусть Interactor сообщит представлению, какие поля доступны для редактирования.
  • Позвольте себе нарушать определенные правила, потому что они уникальны или просто не подходят для всех других примеров, которые у вас есть. Вы никогда не найдете идеальной ситуации.
  • Постарайтесь дать пользовательскому интерфейсу как можно больше простых инструкций.

В общем, старайтесь сохранить как можно больше логики UX в представлении, пока вы не допускаете попадания в них бизнес-правил (таких как вызовы API, проверки разрешений и т. Д.). Это во многом поможет вам сохранить ваш код чистым и без ошибок.

Я надеюсь, вам понравилась эта статья, и я надеюсь, что вы кое-что узнали. Поделитесь своими вопросами или комментариями.