Иллюзия бинарного выбора (про галочку)
Пора записать на бумагу любимый «пример с галочкой».
Приходится проектировать системы, модели, взаимодействие. Цифровой мир научил нас, что правит бинарная логика — да/нет, нули-единички. Цифрам так удобнее жить, не мне вам рассказывать.
Проблема только в том, что в реальном мире бинарной логики — вот по-честному бинарной — довольно мало. Еще меньше её в областях, которыми оперируют живые люди — так органически сложилось. И подобные «упрощения» модели мира часто ведут к граблям и узким местам: что как раз требуется умному человеку знать, и учитывать в проектировании.
А если у вас в руках уже готовая модель или система, реальная или умозрительная, с которой что-то не так — первым делом лично мне нравится пробежаться по вот таким «простым» бинарным выборам и поисследовать чутка, а знал ли автор, с чем вообще работать предстоит.
Пример про галочку многие знают кто у меня учился (ну или просто рядом стоял). Он несложный, ха-ха.
Галочка — это то, что мы/вы/все привыкли видеть даже здесь в интернетах, бинарный чекбокс. От такой.
Смотрите, сейчас накидаю панамку — давайте разбираться, сколько состояний можно предположить даже в таком простом кейсе, когда это просто выбор чего-то, из чего-то, для чего-то. Я буду их отмечать поштучно.
- Состояния «да» (1) и «нет» (2). Их видимо автор имел в виду.
- Состояние «серое», просто не трогай (3), не задавать состояние вообще, не делать выбора. Когда-то в формочной винде оно было стандартом, сейчас в вебах забыли — имхо, зря.
- Состояние «унаследовать», поставить такое, как у родительского/исходного объекта (4). Это не явное состояние выбора — мы как раз не хотим, чтобы состояние было задано явно, а использовало исходное/родительское, каким бы оно ни было, и как бы не менялось впоследствии. Если задать его явно (1,2), то требование к наследованию соблюдено не будет, непонятно, перекрываем мы выбор или хотим не трогать (иногда пересекается с 3, но не всегда).
- Состояние «да, для всех» и «нет, для всех» (5,6). Дихотомия здесь в том, что есть опция применить выбор только в текущем случае (1,2), или расширить выбор на какую-то дополнительную группу объектов — например, вложенных или связанных.
- Состояние «поставить дефолтным», выставить по умолчанию (7). Может попадать в случай «серого» (3), а может и не попадать — например, когда дефолт задается где-то отдельно, или вообще является ситуативным (иногда поступаем так, а иногда иначе).
- Состояния «неизменяемое да» и «неизменяемое нет» (8,9). Когда в данной ситуации выбор вообще недоступен и/ли задан где-то извне, а мы просто для информации статус хотим показать. Может пересекаться с (5,6,7), а может и не пересекаться. Обычно решается атрибутом disabled — как раз бинарным. Но это всё еще та же галочка.
Где-то на этом этапе студенты обычно начинают задумываться :))
- Состояние «указать своё» (10), оно же «другое». Вы все встречали его в опросах, например, когда простых вариантов да/нет недостаточно, и хочется в таком случае запросить подробностей.
- Состояние «да, но не все» (когда применяемых элементов много), вы все о нем задумывались, когда надо выбрать часть элементов из списка. Это варианты «все выбранные» и «все, кроме выбранных» (11, 12).
- Два состояния «врЕменного выбора», встречаются редко, но всё же. Аналог ненавистного маркетингового «напомнить позднее», вы все его видели когда некомфортный (автору системы) выбор про отказ от рекламных рассылок применяли. Пространство вариантов здесь обогащается разницей между «нет, напомнить позже» и «нет, навсегда» — соответственно, +2 врЕменных варианта добавляются (13,14), поскольку ваше желание системе как-то придется хранить и запоминать.
- Состояние «автоматически» (15), которое по сути тоже является явным выбором. Вы хотите, чтобы некоторая автоматика, логика или эвристика приняла выбор за вас. Это не «серое» (3), поскольку выбор про применение автоматики (а не «оставь как было», дефолт) — это тоже выбор.
- Выбор «задним числом», обратная сила примененного выбора по множеству. Я ставлю галку, она должна применяться только к новым операциям начиная с текущей, или надо будет сходить, и что-то изменившееся поменять задним числом везде, где актуально? Разница между «да, для новых отсюда и далее», и «да, для всех вообще». Пишем еще +2 варианта (16,17).
- Новый дефолт: «да, и больше не спрашивать». То есть явный выбор «сделать указанный вариант выбором по умолчанию». Запишем как 1 вариант (номер 18) для простоты.
- Хорошо, мы такие красивые, забрали выбор. А сможем ли мы его реально применить или выполнить? Не попробуешь, не узнаешь. Что, если что-то пойдет не так, а операций предполагается много — ?
Это так называемый «force-flag»: продолжать работу/пропустить ошибки, или же тормознуться при сомнениях. Есть здесь же иногда фантомный третий вариант «вернуть как было» (откатить, не оставлять систему в неконсистентном состоянии), но как минимум 2 опции присутствуют — значит (19,20).
Но это еще не всё, потому что реальная жизнь заковыристее.
А вариантов обычно больше (и они лежат даже ближе к поверхности).
Что еще бывает
О-о-очень во многих случаях простое «да» элементарно распухает до некоторого количество этих самых вариантов «которые да».
Заказ пользователя готов? Да. Что значит «готов»?
Да, принят. Да, обработан. Да, отгружен. Да, оплачен. Да, закрыт. Да, удален.
То же самое с пространством вариантов «нет».
Более того, выясняется, что бывают финально-негативные кейсы: да, заказ обработан, и это спам (т.е все-таки нет?). Да, заказ обработан, принят, но как дубликат (это да или нет, вы мне расскажите?). Да, поставлен в очередь (и возможно все-таки нет, но узнаем позже).
Когда в рассмотрении появляется третья пачка нестабильных вариантов «да, приняли, думаем», «да, в работе» и «да, ждём ответа» — подобный элемент данных успешно эволюционирует до описательного поля «статус», и часть проблем уходит. Это хороший вариант.
Почему часть? Потому что у статусов тоже есть ряд бинарных (хе-хе) характеристик: положительный/нет, финальный/нет, изменяемый/нет, наследуемый/нет ну и так далее. Тут можно поиграться.
Но всем сразу стало легче дышать, когда рамки выбора расширились.
Возникает коллизия тогда, когда вместе с означенным выбором надо пойти, и что-то сделать прям сейчас. Вы все это видели в «управляющих чекбоксах» например в выборе списка файлов в ОС, или в выборе писем в гугле. Основной критерий — выбор у нас не просто декларативный, а за ним последует какое-то действие (например, повлиять на другие чекбоксы, что-то посчитать итп).
С элементами управления сложно. Тот же гугл свою галочку-в-списке дизайнил и тестил месяца три (и правильно), чтобы понять, насколько пользователь вообще поймет, что от него хотят. Оказалось, что понятно не всегда: поэтому к ней есть более продвинутая выпадашка, в которой можно уточнить, что вообще за действие подразумевается.
Так что у нас тут не просто чекбокс, а целый компонент с пачкой логики под ним.
Еще в гайдах винды и макоси нам ясным по белому написано, что комментарий к чекбоксу не должен начинаться с отрицания. И это правильно.
Здесь всё просто: если ваш «чекбокс» содержит в подписи «не сохранять изменения» или «игнорировать последствия ядерного взрыва» — или инвертируйте выбор + замените формулировку, или выбросьте его нафиг, переделайте. Лучше лишний раз подумайте, всё ли вы учли, раз такая ситуация вообще встретилась.
Что делать
Первое, что посоветую — это не путать пользователя. Если есть хоть небольшой шанс, что бинарный выбор может быть понят неправильно — замените несчастную галку на текстовое описание операции.
От замены чекбокса на выпадашку/радиокнопку с явным текстом еще никто не умер, зато вы не заставите пользователя лишний раз угадывать поведение системы. Доверие и прозрачность — важно.
Вдобавок убережет вас от идиотических вариантов «отменить» — «да, отменить» + «нет, отменить» и их аналогов.
Второе, что посоветую — это посмотреть в реальный мир, то есть проработать юзкейсы. Посмотреть, как должна вести себя система в реальном мире, а не в упрощенной модели в вашей голове. Здесь чаще всего вы рискуете получить пространство статусов и вариантов — ну то и к лучшему.
Третье. Если у вас хоть как-то затронута иерархия и связи объектов (хоть во вложенности, хоть иначе) — всегда разделите поведение явно. Или, если очень не хочется — напишите внятным текстом, как будут применяться изменения, и как с точки зрения связей будет/должна себя система повести. Прозрачность и понятность еще никому не мешала.
Ситуация «давай посмотрим», четыре. Если предполагается, что выбор может быть не окончательным — продумайте поведение системы, когда можно или посмотреть результат, или передумать, сделать выбор не финальным. Вы его уже знаете из виндовой кнопки Apply (применить), когда выбор выполняется, но процесс выбора не заканчивается.
Есть усложненный вариант, когда система позволяет либо спрогнозировать поведение («85 объектов будут затронуты фильтром»), либо посчитать модель на шаге финализации, и показать, что же реально будет происходить. Будет интересно еще вот тут почитать про стоимость действия и операций.
К слову сказать, универсальные системы хранения данных обычно «булевые значения» не хранят битово. Выделят какое-то пространство с запасом.
Практически всегда в не-специализированных БД для bool и подобных на хранение выделяется байт (так читать удобнее) + есть третье состояние null, а ваша галка представляет собой что-то вроде однобайтного целого int (1). Есть еще биткарты (bitmap), которые тот же самый байт в инте, только функциональность похитрее, ну и 8 несвязанных бит под ваши нужды.
Хранение «с запасом» пригодится, если впоследствии выяснится что-то неявное, из списка выше. С индексами конечно хитрее, но на то они и индексы — их и перестроить можно, пускай роботы парятся. Опытный человек чаще поставит int/enum вместо bool, потому что железке пофиг — она знает, сколько там вариантов реально лежит — а вот человеки могут потом и передумать.