Константно-переменный дуализм.
Настройки у программ существуют давно, от самых древних ассемблерных программ, имеющих в своём коде константы в качестве настроек. Обновление таких программ происходило с полной заменой кода, поэтому изменение системы настроек не влекло отрицательных последствий. В худшем случае, приходилось переучиваться пользованию интерфейсом. Положение стало меняться при преемственности типов данных и настроек программ. В своё время Microsoft уделяла огромное внимание совместимости программ и данных в операционных системах, в чём была их сильная сторона. Да и сейчас поддержка режима XP Mode — это продолжение политики совместимости. Применительно к веб, пользователям часто встречаются ситуации, когда данные и настройки у них остаются от прежних версий (в куках, в сторонних базах данных), а версия программы — на сервере или скрипт на клиенте — изменяется. Возникает проблема совместимости версий программ и данных и необходимость выработки некоторой политики поддержки совместимости. Новая версия может не устроить по совместимости с браузерами (из-за багов или неполной поддержки), тогда откат к прежней на время исправления багов не должен сопровождаться потерей или искажением настроек, при том, что количество их может меняться с изменением версии.
Требуется подход не просто к настройкам и их организации, а к проблемам смены версий и преемственности настроек. Программы в вебе развиваются мелкими шагами, смена версий происходит часто. Такой же подход к работе с настройками относится и к другим программам, не связанным с вебом, но имеющим частую смену версий. Аналогичные рассуждения можно отнести к форматам всех данных в программах, не только к форматам хранения настроек.
Основные тезисы, которые выводятся и прорабатываются в статье:
*) настройки — объект для программ и клиентских веб-приложений с похожими моделями поведения;
*) настройки по умолчанию — не что иное, как рекомендации производителя по настройкам;
*) изменение настроек при смене версий программы — это изменение рекомендаций производителя; могут существовать смены версий только ради смены настроек;
*) настройки пользователя — рекомендации пользователя себе;
*) экспорт-импорт настроек — возможность обмена между пользователями, сохранения рекомендаций, переноса между компьютерами;
(Далее — по смене версий.)
*) изменение семантики настройки производителем (при смене версий) — ошибка, вводящая нарушение логики при апгрейде — даунгрейде;
*) при хранении 2 состояний настроек с переключением на кнопке «возврат» возможен ряд функций без усложнения интерфейса: проба новых настроек при смене версии, откат к старым настройкам, проба и хранение сторонних рекомендаций;
*) для сохранения простоты нежелательна более сложная система манипуляций. Существует экспорт-импорт для всех расширенных действий;
*) возврат к настройкам производителя — отдельная функция («Сброс»);
*) рекомендации производителя «зашиты» в программу, поэтому не занимают отдельного места, их может быть несколько (профили настроек), для сохранения других рекомендаций есть кнопка «Сохранить»;
Увидев глубину и общность проблемы настроек и рекомендаций, обрисуем круг требований, которые попытаемся поддержать. Нужно иметь достаточно простую и универсальную систему настроек, которая бы удовлетворительно работала и развивалась при смене версий. В результате получится практичный алгоритм поддержки обозначенного круга требований. После построения алгоритма покажем реализацию подсистемы настроек на *.user.js и отдельную процедуру на *.js. Она была сделана из соображений, что лучше сразу описать регламент работы с настройками для себя и пользователей и не отходить от него, чем вырабатывать потом, исправляя собственные ошибки.
При смене версий меняется всё — программа, настройки, логика и семантика (содержательные значения настроек). Смена версий — это ещё одно измерение развития логики, которое учитывает приход данных из старых версий, настроек из них, а иногда — наоборот, из новых версий, если происходит даунгрейд или перенос настроек на другой компьютер, другую версию программы. Смоделировав ситуации смены версий, начинаем понимать, каких правил необходимо придерживаться, а какую свободу можно оставить. Или, говоря другими модными словами, создадим паттерн проектирования «Настройки».
Касаясь проблем совместимости, каждый разработчик живо вспоминает классический кошмар, который устроила фирма Adobe с версиями программы Photoshop,чтобы поухватистее вывернуть карманы клиентов. Тут, скорее всего, она действовала с умыслом, тем более, что история повторяется с их другими дорогостоящими программами. Поработали 1-2 года с флагманской программой индустрии — платите следующий взнос. Такого подхода не могут позволить себе мелкие программы и сервисы, а, наоборот, смену версий требуется делать бесплатно и очень часто, чуть ли не с каждым выходом новой версии какого-либо браузера. Необходима чёткая и обоюдно комфортная смена версий с поддержкой совместимости настроек. (А также, один к одному — форматов данных и их семантики, но будем исследовать только настройки.) При этом разные пользователи могут обновиться далеко не от предыдущей версии, а бОльшая часть пользователей не будет утруждать себя работой с настройками, или некоторые настройки будут отсутствовать вообще, но быть автоматическими — например, сохранение параметров расположения окна в системе.
Система сохранения и перехода между версиями учитывает эти факты и обеспечивает:
1) хранение и восстановление настроек в текущей версии программы;
2) при обновлении программы — не забывать старые настройки на случай возврата к старой версии (хотя бы на 1 шаг назад);
3) неизменность смысла старых настроек в среде сколь угодно новой версии (семантика не должна меняться; почему — разбор ниже);
4) не напрягать пользователя слежением за версиями и не принуждать его делать какие-либо действия (лучшее — враг хорошего; нет необходимости — не отвлекать);
5) не стремиться к гигантизму и хранить разницы (диффы) в настройках — шаг оптимизации.
При работе с версиями скрипта JS, который на клиенте тоже оказывается полноценной программой, обнаружилась проблема сохранения настроек пользователя при смене версий. Сразу же вспомнились вопросы совместимости версий Фотошопа и некоторых других монстров. И подумалось, что решить проблему правильной работы с пользовательскими настройками лучше в зародышевом состоянии, пока скрипт не пошёл в массы и не появилось множество недовольных пользователей, которые вынуждены будут вручную, по памяти настраивать новую версию программы, или, ещё хуже, старые настройки войдут в противоречие с новой версией, возникнет нетестированный баг. Предлагать пользователю «сбросить всё и установить по новой» — конечно, известный и безобразный способ решения таких проблем, с которым искушённые пользователи смирились. (В самом деле, что они могут сделать с производителем Фотошопа, когда пользы больше, чем неприятностей?) Лучше сразу заложить в проект технику разрешения конфликтов настроек при смене версий. Так были построены правила работы с версиями и настройками, которым мог бы следовать проектировщик программы.
Во второй части в следующей статье — реализация тестовой страницы на JS — готовый скрипт, в котором можно проверить работоспособность подхода. В скрипте настройки пользователя — произвольная многоуровневая структура, поэтому её можно портировать в различные проекты, в которых тоже задумались о необходимости корректной смены версий. Или, используя первую и вторую части, написать логику на другом языке. Усилия не пройдут даром и можно сэкономить на труде дизайнера по заготовке красивой таблички такого содержания:
Как уже упоминалось, смена версий программы — выход в новое измерение, от решения проблем с багами кодов в одной простыне программы к решению проблем её развития. Разработчики могут не сразу осознать специфику этой области, если долго работают над рядом других очень важных функций своей программы, а вопрос о смене версий на первом плане не стоит. Но когда-то он встаёт, и начинается эволюция.
Что было в эпоху динозавров: программа при установке новой версии начинает работать плохо или вообще не работает. Рекомендация: перед установкой новой программы удалите предыдущую версию (и очистить реестр и файлы папок от данных из старой версии программы). В особо тяжёлых случаях — не открывать данные, созданные в старой версии программой новой версии.
Другой древний вариант: при переходе к новой версии программа не спрашивает, какие были настройки — она ставит дефолтные, а пользователь устанавливает их вручную. Способ обхода проблемы с программой: иногда подменяют данные в ini-файлах, реестре (win detected), конфигурационных файлах, и этим спасают себя от части работы по настройке. Ещё варианты решения — хранить программу с образом виртуальной ОС, в которой она установлена; ставить redisributable пакет, в котором создали работающий настроенный образ программы, а затем его упаковали для повторной инсталляции; использовать тихую инсталляцию с параметрами настройки, предусмотренную в довольно многих серьёзных программах.
Случай более приспособленных форм жизни: программа начинает понимать старые настройки, оставшиеся от предыдущей версии. Что для этого нужно делать при создании программы?
Речь будет идти, то о программах, то о вебе и сайтах, потому что там и там предмет обсуждения один — пользовательский интерфейс, версии продуктов. Только входной поток иной. Для программ — это настройки и их наследование разными версиями, для веба — это страницы данных и их отображение разными браузерами. Иногда эти данные настолько похожи на настройки, что их начинают так называть, а веб-страницы или виджеты на них начинают называть приложениями.
Хороши были времена, когда новая версия программы имела полностью новые настройки. Тогда вопроса наследования просто не стояло. Как в эпоху динозавров, новая версия просит всё-всё установить заново или пользоваться настройками по умолчанию. Но наследственность как базовое свойство развивающихся систем быстро изжила этот недостаток. От программ стала требоваться не только совместимость по данным, но и совместимость по настройкам. Сначала динозавры, а потом и куры начали требовать яиц, положив начало вопросу о том, что раньше появилось. Установил новую версию — не потерял заданные ранее настройки, скажем, размеров окна и шрифта. Но система настроек от версии к версии имеет свойство развиваться, поэтому приходится учитывать развиваемость программ, эволюцию жизненных форм. И ещё, программа может обновиться до какой угодно версии, даже до более старой, а значит, она должна корректно использовать не только любые предыдущие настройки, но и настройки в любом будущем формате!
Например, в системе тегов и атрибутов HTML, XML имеется правило: если встречается новый атрибут или тег, он игнорируется (его смысл неизвестен). Очевидно, и в системе настроек логично при появлении новых — игнорировать их. Так, в HTML-страницы, а ещё ранее — в настройки программ для разных версий той же программы ввели самое первое и простое правило совместимости.
Однако, даже при неизменных настройках программа может (по ошибке) начать интерпретировать некоторую настройку иначе. Если программы — от разных производителей, которые от пользователя получают одинаковые данные, они могут интерпретировать данные в несколько разных смыслах. Например, у одних размер экрана учитывает первый монитор, у других — максимальное измерение из группы всех подключённых мониторов. Если снова обратиться к аналогии с HTML, то это в HTML известно как проблема совместимости представлений данных в браузерах. Даже одинаковые данные (таблицы, поля ввода) вызывают немного разное отображение результатов.
Есть проблема — взялись за её решение. Теперь есть «совместимое» представление, а есть строгое. При большом количестве версий браузеров количество строгих представлений растёт, а совместимых — растёт ещё быстрее (в квадрате), т.к. каждый браузер и версия будет иметь своё представление о совместимости.
Если бы мы приняли такой подход для совместимости настроек в программах, то надо было бы написать правила переноса настроек между версиями. Потому что установка версий не всегда происходит последовательно. Есть перескоки через версии, есть возвраты к прежним (например, по причине багов новых версий). Понятно, что этот процесс, даже если будет начат на уровне 2-3 версий программы, быстро наскучит разработчикам, и они начнут поддерживать только самые актуальные совместимости — переход от старой версии к новой.
Придерживание единой линии поведения — лучшее средство постройки Вавилонской башни.
Если в новой версии программы (или даже при новом запуске) перестали поддерживаться значения, установленные в настройках ранее, программа должна достойно на это отреагировать.
Например, в ту же эпоху динозавров, все (или многие) программы считали, что должны появляться в середине экрана и занимать примерно 50% ширины экрана. Если пользователь решит сменить положение и размер окна, было совсем не обязательно, что в следующий раз программа откроет окно на новом месте. Почему так?
Если программа запомнит положение и размеры окна, то в другой раз она может запросто оказаться за пределами экрана, если сменили монитор. Поддержка программы усложняется, а эволюция не всегда поддерживала сложные формы жизни. Увидеть монитор и окружение — это долгие процедуры, которые в неразвитом API создавали ад для разработчика, и они (или эти идеи у них) вымирали, даже не дав потомства — работающих в этом ключе программ. Программы могли быть крайне сложными, но иметь примитивный интерфейс, потому что каждое дополнительное действие над работающей программой могло дать серию багов и необходимость нового цикла тестирования.
Но шли годы, и с появлением более совершенных API и сред разработки у разработчиков прибавилось сил и уверенности в будущем.Динозавры обросли перьями и начали летать. Теперь вполне реально увидеть, что если программа приняла недопустимое значение из настроек, она не загнётся в агонии и не будет сметена безжалостной рукой природы пользователя, а поступит разумно.
Разумные альтернативы:
1) выбрать ближайшее похожее значение;
2) выбрать значение по умолчанию;
3) предложить выбор комплекса связанных значений, поскольку изменение параметра иногда требует изменения других.
Разумная программа реагирует на недопустимое для данной версии значение.
Это — наиболее сложный случай запутывания программой своих прежних настроек. Если контроль настроек базируется на передаче имени + значения каждой настройки, то искажение смысла (т.е. действий программы) значения приведёт к неконтролируемому пользователем изменению поведения программы.
Например, ранее 1 — это открывать на весь экран, теперь — в большом окне.
Изменилось понимание дефолтной настройки, а в файле настроек не хранятся дефолтные значения. Очевидно, что наличие таких изменений смысла приведёт к ошибкам и потребует исправления.
Таким образом, как браузеры стремятся белое называть белым (правда, не всегда, например фон страницы кастомизируется), так и программа стремится в настройках видеть то, что в них было заложено по смыслу. (Если некоторая переменная windowWidth указывала на ширину окна, нет смысла в новой версии ей указывать количество работников предприятия.)
Не все поступают так откровенно. Вот — (гипотетический) пример очень скрытного изменения смысла. До версии N ширину окна в ОС разработчики считали по внутренней границе окна, а с N-й решили, что правильнее считать вместе с бордюром. И тогда, если ранее пользователь устанавливал в прежних версиях ширину 100, в N-й он получит внутреннюю ширину (по прежнему смыслу) 98, 96 или другую, зависящую от настроек ОС. На месте разработчиков, при необходимости ввода другой ширины, для неё нужно выбрать другое название (имя, ключ в настройках). А прежнее за ненадобностью можно навсегда удалить (или оставить процедуру конвертирования, только для совместимости версий).
Смену семантики значения настройки нужно рассматривать как ошибку разработчика.
Наиболее безобидный случай из приводящих к нарушениям работы.
К примеру, есть 2 чекбокса, оба выбраны. Первый чекбокс пользователь обнулил, второй его устроил (а если бы не был выбран, пользователь его бы выбрал, потому что хочет значений, 0, 1). В следующей версии производитель сбросил оба чекбокса. Пользователь в сохранённых настройках имеет только выбор первого: 0, Х, где Х означает предоставление выбора производителю. И имеет проблему, потому что в интерфейсе не было и нет возможности закрепить дефолтное значение второго чекбокса.
Результат — неодинаковая работа системы настроек для разных пользователей. Всё в порядке с пассивными и проблемы для активных, не могущих зафиксировать выбор дефолтной настройки. (Из этого следуют рекомендации профессионалов в смежных областях — CSS, например — учитывать действия атрибутов по умолчанию, прописывая их явно (CSS reset), чтобы в случае изменения политики умолчания в будущем не возникло иного понимания атрибутов.)
Выход в том, чтобы давать возможность фиксировать настройку, даже если она находится в значении по умолчанию. Другими словами, пользователь выразит явное желание установить это значение, а не пассивное согласие на настройки (рекомендацию) производителя. Следовательно, мы не можем, как это обычно делают, не дать возможность указать фиксацию настройки пользователя в том же значении, в котором она находится сейчас. (Это и был тот самый тонкий момент, сбой системы без фиксации дефолтных настроек, который заставил разобрать проблемы.)
В системах голосования, если кто-то уже проголосовал так же, как и пользователь, вовсе не значит, что пользователь не может так же проголосовать. Аналогично — и в настройках. «Облегчение» выбора настройки по умолчанию без возможности подтверждения, фиксации её в интерфейсе является «медвежьей услугой» и источником проблем. С фиксацией — соблюдается баланс рекомендации производителя и выбора пользователя.
В нашем случае с чекбоксами, нужно не просто давать изменять настройки, но и указывать фиксацию каждой. Для оптимальности процесса, если пользователь что-то изменил, это уже означает фиксацию. Зато, её теперь нужно смочь снять — вернуться к значению по умолчанию, опять же, для каждой настройки. Аналогично — вернуться к выбору другого авторитетного пользователя, если система будет поддерживать не один профиль настроек, а импорт других авторских настроек в том числе.
Далее, производитель делает смену настроек неспроста, поэтому полезно, чтобы о ней узнали, в ненавязчивом режиме. Если пользователь заинтересовался настройками, он должен увидеть то новое, что произошло с ними с момента его последнего захода в настройки. Если он зашёл первый раз, ничего не выделяется, всё ново. Если что-то менял или фиксировал ранее, а производитель изменил свою рекомендацию — пользователь увидит изменение. Разработчик обеспечивает указание на смену дефолтных настроек.
Вопрос плавно подходит к системе помощи, подсказок, информации и выбору рекомендаций. Место оповещения об изменении настроек — также лучшее место описания настроек вообще или подсказок по ним.
Следовательно, интерфейс выбора настройки обрастает 2 элементами: фиксацией/снятием фиксации и подсказкой как по истории изменений, так и описанием настройки вообще.
Примерно так (просто перечислены элементы управления):
В квадратных скобках — значение чекбокса; знаки вопроса — подсказки; цвет вопросов может указывать на то, что системе есть, что сказать об изменениях. Значки «fix/unfix» — ссылки на фиксацию дефолтов или возврат к ним. Если рекомендателей несколько, значки фиксации усложняются. Можно иметь в ряду настроек — указание о пользователях, выбравших эту настройку (сколько и с каким статусом), и среди них — значение настройки разработчика. С таким подходом настройки начинают напоминать оценки социальной сети. Что ж, может быть, это — один из вариантов коллективного использования программ.
Если механизма фиксации нет — имеем проблемы интерфейса и логики. Впрочем, в интерфейсах чекбоксов в ОС эту проблему решили перебором 3 состояний: 0, 1, default. В HTML её тоже решить можно применением disabled и циклическим перебором с помощью JS. Тогда, по крайней мере, для чекбоксов и радиокнопок не потребуется элемент fix/unfix.
В этом случае, очевидно, требуется откат к последней устраивающей версии. При этом некоторые данные могут остаться новые (обычно из предметной области), а настройки программы следует брать из старых настроек, но при этом часть новых настроек тоже может оказаться полезной и сохраняется как рекомендация для старых или для новой стабильной версии.
… Второй раз появилось слово «рекомендация». Это непривычно для настроек. Есть «дефолтное значение» как рекомендация разработчика, есть (бывает) сброс к дефолтным значениям, но нет рекомендаций. Есть импорт настроек, но тогда все настройки примут новые значения, и сравнивать можно лишь с тем, что было до смены. Рекомендация — это возможность попробовать изменить настройку или их группу и немедленно увидеть и сравнить результат.
Идея рекомендации и сравнения — очень мощная. (Так, кстати, работала и эволюция в эпоху динозавров.) Ряд сайтов магазинов старается максимально удобно представить товары для сравнения. Это — та же рекомендация выбора, как рекомендация установить некоторую настройку. Поэтому, если мы осилим показать в программе последствия рекомендации настройки, откроются перспективы сделать ещё очень многое и неожиданно полезное тем же инструментом.
Вернёмся к откату версии. Чтобы полноценно сделать откат, программе не следует безоговорочно забывать старые настройки. Лучше, если она помнит последний шаг смены версии программы. Если пользователь не успел опробовать все возможности прежних версий — пусть помнит и прежние шаги. Тогда он получит возможность выявления последней правильно работающей версии. Очевидно, это будет полезно для разработки и тестирования. В эпоху динозавров такого ещё не было…
Правда, не хотелось бы так усложнять интерфейс выбора настроек, чтобы оперировать с несколькими наборами сразу. Это хорошо для профессиональных тестировщиков и других разработчиков, но есть задача сделать массовый элемент управления, поэтому лучше, если он будет максимально простым и гибким. Обычно гибкость достигают экспортом-импортом настроек. С ним пользователь в отдельном месте вне программы может реализовать любую систему каталогизации и хранения версий, что опять-таки, сложно. В дополнение ко всесильной системе экспорта, хорошо бы иметь простую подсистему, не требующую прибегания к экспорту, насколько это будет возможно.
Попробуем хранить не 1 список настроек, а 1 или 2, относящихся, например, к текущей версии и к прежней. Или текущую версию и экспериментальную. Тогда в интерфейсе управления нам надо иметь всего одну кнопку (как в калькуляторах — кнопку «М» (память) ), чтобы переключаться между своими настройками и не путаться в них, не вводить имена и не строить «менеджер». Для всего, что больше — имеется к услугам экспорт. Это фундаментальное упрощение интерфейса, будем надеяться, позволит построить практичную модель и останется интуитивно понятной, как кнопка «М».
Отправная точка такой идеи — в том, что большинство действий, в том числе проверка новой версии и откат к старой, не требуют оперирования с более чем 2 списками настроек. Проверка чьей-то рекомендации с хранением одного «комплекта» своих настроек — тоже. Далее опишем техническую реализацию.
Хранить настройки — означает разместить их в localStorage, чтобы использовать в пределах браузера, независимо от перезагрузок. Кроме хранимых наборов, можно временно тестировать другие настройки, не затрагивая хранимых. Сохранение делается нажатием кнопки «Сохранить» или загрузкой новой версии программы. Откатиться можно без использования новых настроек (по умолчанию) или с использованием (указывается при откате). Перейти к произвольной другой версии — с выбором из тех же двух. Новые настройки выбираются, если номер версии — больше версии настроек нового комплекта. Если 2 комплекта одинаковы, всё равно считается, что их — два. Хранятся — как разности, поэтому, чем меньше различий, тем меньше занято места. Текущие настройки хранятся как разность между рекомендациями производителя и пользовательским выбором. Разность понимается как указание настройки в случае, если она выбрана пользователем (даже если совпадает с дефолтной) и неуказание, если иначе.
Есть ещё один технический момент, что в браузере наиболее просто реализовать чтение настроек в начале загрузки страницы, а затем считать их константами. Процесс смены настроек требует перезагрузки, поэтому какое-либо тестирование требует сохранения комплекта настроек, а для незабывания настроек пользователя нужно хранить 2 комплекта и использовать затем ту же кнопку «Вернуться» для восстановления.
Что можем делать с 2 списками настроек? Соберём полный список.
1. Перейти к новой версии программы и загрузить новые настройки из неё. Старые останутся для отката, а новые — для продолжения работы.
2. Если выполняется откат, во втором наборе сохраняются данные новых настроек, если их пользователь изменял. При повторном переходе к той же или более новой версии используется наработанное.
3. Сохранение другой рекомендации (импорт — сохранить) стирает данные для отката, чтобы настройки другой рекомендации могли исследоваться после перезапуска браузера.
4. Тестирование других настроек (настройки — тестировать — сохранить), которые можно продолжить после перезапуска браузера и после перезагрузки страницы.
Ничего не мешает тестировать настройки без их сохранения, но тогда мы ограничены пределами времени до перезагрузки страницы. Так можно протестировать самые простые и немедленно действующие настройки. Большинство настроек устанавливают свои значения в программе как раз в начале загрузки страницы, поэтому без сохранения в localStorage они не смогут быть протестированы. Механизм сохранения 2 комплектов настроек позволяет тестировать без таких ограничений, что создаёт хорошее дополнение к возможностям скриптов.
Потому что, с одной стороны, есть стремление к простоте и понятности настроек. Чем меньше сущностей, тем лучше. Самое простое — иметь один комплект настроек — «мои». Для минимума манипуляций вводятся настройки «другие» и единственная кнопка переключения «Возврат». Запутаться сложно, а функций добавляет. Есть и другая сторона вопроса.
Казалось бы, что трудного в том, чтобы перенести настройки на сервер в базу данных, а там — работать с ними сколь угодно сложно: хранить для каждой версии, пересматривать историю? Тут подходим к паре других сущностей нашей задачи. Первая — предполагается хранение настроек независимо от авторизации на сервере. Без авторизации или для другой задачи — в качестве настроек юзерскрипта на клиенте, где авторизации тоже не предполагается. Вторая сущность постановки — хранение настроек в текстовом виде для экспорта предполагает наиболее компактную форму хранения, поэтому надо иметь наиболее короткую и редактируемую вручную строку. Поэтому есть 2 комплекта, и то, хранятся только разности настроек, чтобы данных было меньше.
Этот раздел требует отдельной статьи и другого блога. То, что делали раньше, называется «Архитектура программы», но такого блога нет, есть только тег «Архитектура». Чтобы описание было законченным, опишем результаты кратко, словами, без иллюстраций, а подробности вынесем в другую статью. Получилось, как обычно в текстовых описаниях интерфейсов — скучно, трудночитаемо и предназначено только тем, кто подобным занимался и хочет сравнить результаты.
В начале, без особых действий пользователь не получает никаких дополнительных элементов в интерфейсе нигде, до тех пор, пока он не начнёт пробовать изменить настройки. Нет даже (или не активизирована) кнопки «Экспорт», потому что экспорт пустой строки — это, по умолчанию, согласие с настройками производителя.
Есть кнопка (или поле ввода) "Импорт", по которой через поле ввода загружаем строку JSON или нечто подобное для смены настроек. Немедленно видим отражение этих настроек в интерфейсе настроек, название рекомендателя, версию настроек + версию программы, для которой они были сделаны. Нет ничего необычного, если в другую версию программы (старую или новую) загрузим настройки произвольной версии. Сработают те, ключи которой найдутся в текущей версии программы. Появляются призывные кнопки "Протестировать" и "Отмена". Вторая восстанавливает просмотр своих настроек и ничего не делает, а первая сохраняет как актуальную — загруженную импортированную версию, а как резерв — текущую, стирая при этом любую другую резервную версию, если она была (одноуровневый стек).
Наличие сохранённой резервной версии отмечается в панели настроек. Поэтому, как только нажмём «Протестировать» — на кнопке "Вернуться", которая придёт на смену, появится информация о сохранённой версии пользователя (её может не быть, если настроек пользователя не было, но режим «тест» всё равно включается). В интерфейсе страниц появляется постоянно висящая ссылка на блок настроек"тест" (как напоминание о пришедшей почте).
При нажатии на «Вернуться» получаем возврат настроек для программы (обычно приводятся в действие после перезагрузки страницы), а в панели настроек — настройки для теста и кнопки «Протестировать»-«Отмена» — прежний шаг операции.
Если загружается новая версия скрипта (программы), что отмечается по анализу localStorage, она действует на состояние настроек аналогично, если они были когда-то изменены. Создаётся копия старых настроек и… аналогично «тест», появляется нотификатор "новая версия". Если этого не сделать, изменение поведения программы вызовет недоумение пользователя. Здесь возникает вопрос, показывать ли эту кнопку всем или только интересующимся. Скорее всего, второе, а для всех — нотификатор прячется в панель настроек.
Если смена версии происходит многократно, ничего особо не меняется, только значение новой версии скрипта (и новые настройки). Если пользователь изменил настройки для новой версии, появляется кнопка "Сохранить", по клику на которой появляется 2 группы настроек: к новой версии и к старой. «Вернуться» вызовет ротацию версий: слово «Вернуться» сменится на "Применить новые", чтобы снова переключиться на новые настройки, как в случае «Протестировать». Чтобы вернуться к старой программе, нужно нажать появившуюся кнопку "Откат", который произойдёт на версию, номер которой запомнен при последней правке старых настроек. (Этот механизм требует доступа к любой выпущенной версии.) Для переключения к другим версиям — спадающий список номеров.
Если пользователь изменил комплект настроек ещё раз, когда 2 комплекта сохранены, но имеется 3-я версия программы, нового сохранения в стеке не будет, изменится только текущая группа (после нажатия «Сохранить»).
Кнопка "Подтвердить настройки" — чтобы отказаться от хранения запомненного состояния — отката, оставшись на новой версии (значок «новая версия» удаляется). Если состояние одно, кнопка называется «Стереть настройки» или "Сброс", и память очищается от настроек, используются настройки по умолчанию. «Сброс» защищён от случайного нажатия.
Учитывая ограничение числа комплектов настроек двумя, любое действие тестирования сотрёт настройки отката и не восстановит их по окончанию теста. Чтобы их не потерять, пользуются кнопкой экспорта, которая активизируется при наличии какой-либо пользовательской настройки.
Перечислим ещё раз список кнопок и нотификаторов и их группировку:
* Импорт, Экспорт — для внесения-извлечения настроек в виде строки;
* Протестировать, Отмена — применение настроек, отмена показа импорта в настройках;
* «тест» — нотификатор, подсказывающий, что настройки находятся в режиме теста;
* Вернуться — восстановление своих настроек, выход из теста;
* «новая версия» — нотификатор, говорящий о загрузке новой версии (в панели настроек);
* Откат — аналог «Вернуться» для отката к прежней версии;
* комбо или список версий — к какой версии будет откат;
* Сохранить, Отмена — подтверждение смены настроек от пользователя; аналогично «Протестировать», но без нотификации;
* Подтвердить настройки (вместо «Вернуться») или Сброс — удаление хранимых настроек, применение текущих (или умолчательных), снятие нотификатора «новая версия»;
* Применить новые — после отката — перейти обратно к новой версии настроек.
В следующей серии попробуем увидеть всё это вживую, но, вполне возможно, что в скрипте это ещё не будет работать, а писать статью с разрисовкой интерфейса будет никому не нужно — программирование идёт без макетов. Тогда дождёмся публичного скрипта с описанными механизмами в качестве демонстрации.
Получен интерфейс работы с настройками без технологических изъянов, которые могли быть без учёта развития программы при смене версий. Введено понятие «рекомендация» и предусмотрена возможность работы с несколькими рекомендациями и с форматом настроек, совместимым по версии программы в любую сторону — работающим как с новыми версиями (для апгрейдов), так и со старыми (для откатов).
Настройки у программ существуют давно, от самых древних ассемблерных программ, имеющих в своём коде константы в качестве настроек. Обновление таких программ происходило с полной заменой кода, поэтому изменение системы настроек не влекло отрицательных последствий. В худшем случае, приходилось переучиваться пользованию интерфейсом. Положение стало меняться при преемственности типов данных и настроек программ. В своё время Microsoft уделяла огромное внимание совместимости программ и данных в операционных системах, в чём была их сильная сторона. Да и сейчас поддержка режима XP Mode — это продолжение политики совместимости. Применительно к веб, пользователям часто встречаются ситуации, когда данные и настройки у них остаются от прежних версий (в куках, в сторонних базах данных), а версия программы — на сервере или скрипт на клиенте — изменяется. Возникает проблема совместимости версий программ и данных и необходимость выработки некоторой политики поддержки совместимости. Новая версия может не устроить по совместимости с браузерами (из-за багов или неполной поддержки), тогда откат к прежней на время исправления багов не должен сопровождаться потерей или искажением настроек, при том, что количество их может меняться с изменением версии.
Требуется подход не просто к настройкам и их организации, а к проблемам смены версий и преемственности настроек. Программы в вебе развиваются мелкими шагами, смена версий происходит часто. Такой же подход к работе с настройками относится и к другим программам, не связанным с вебом, но имеющим частую смену версий. Аналогичные рассуждения можно отнести к форматам всех данных в программах, не только к форматам хранения настроек.
Основные тезисы, которые выводятся и прорабатываются в статье:
*) настройки — объект для программ и клиентских веб-приложений с похожими моделями поведения;
*) настройки по умолчанию — не что иное, как рекомендации производителя по настройкам;
*) изменение настроек при смене версий программы — это изменение рекомендаций производителя; могут существовать смены версий только ради смены настроек;
*) настройки пользователя — рекомендации пользователя себе;
*) экспорт-импорт настроек — возможность обмена между пользователями, сохранения рекомендаций, переноса между компьютерами;
(Далее — по смене версий.)
*) изменение семантики настройки производителем (при смене версий) — ошибка, вводящая нарушение логики при апгрейде — даунгрейде;
*) при хранении 2 состояний настроек с переключением на кнопке «возврат» возможен ряд функций без усложнения интерфейса: проба новых настроек при смене версии, откат к старым настройкам, проба и хранение сторонних рекомендаций;
*) для сохранения простоты нежелательна более сложная система манипуляций. Существует экспорт-импорт для всех расширенных действий;
*) возврат к настройкам производителя — отдельная функция («Сброс»);
*) рекомендации производителя «зашиты» в программу, поэтому не занимают отдельного места, их может быть несколько (профили настроек), для сохранения других рекомендаций есть кнопка «Сохранить»;
Увидев глубину и общность проблемы настроек и рекомендаций, обрисуем круг требований, которые попытаемся поддержать. Нужно иметь достаточно простую и универсальную систему настроек, которая бы удовлетворительно работала и развивалась при смене версий. В результате получится практичный алгоритм поддержки обозначенного круга требований. После построения алгоритма покажем реализацию подсистемы настроек на *.user.js и отдельную процедуру на *.js. Она была сделана из соображений, что лучше сразу описать регламент работы с настройками для себя и пользователей и не отходить от него, чем вырабатывать потом, исправляя собственные ошибки.
Круг требований: насколько настройки будут совместимы по версиям программы
При смене версий меняется всё — программа, настройки, логика и семантика (содержательные значения настроек). Смена версий — это ещё одно измерение развития логики, которое учитывает приход данных из старых версий, настроек из них, а иногда — наоборот, из новых версий, если происходит даунгрейд или перенос настроек на другой компьютер, другую версию программы. Смоделировав ситуации смены версий, начинаем понимать, каких правил необходимо придерживаться, а какую свободу можно оставить. Или, говоря другими модными словами, создадим паттерн проектирования «Настройки».
Касаясь проблем совместимости, каждый разработчик живо вспоминает классический кошмар, который устроила фирма Adobe с версиями программы Photoshop,
Система сохранения и перехода между версиями учитывает эти факты и обеспечивает:
1) хранение и восстановление настроек в текущей версии программы;
2) при обновлении программы — не забывать старые настройки на случай возврата к старой версии (хотя бы на 1 шаг назад);
3) неизменность смысла старых настроек в среде сколь угодно новой версии (семантика не должна меняться; почему — разбор ниже);
4) не напрягать пользователя слежением за версиями и не принуждать его делать какие-либо действия (лучшее — враг хорошего; нет необходимости — не отвлекать);
5) не стремиться к гигантизму и хранить разницы (диффы) в настройках — шаг оптимизации.
При работе с версиями скрипта JS, который на клиенте тоже оказывается полноценной программой, обнаружилась проблема сохранения настроек пользователя при смене версий. Сразу же вспомнились вопросы совместимости версий Фотошопа и некоторых других монстров. И подумалось, что решить проблему правильной работы с пользовательскими настройками лучше в зародышевом состоянии, пока скрипт не пошёл в массы и не появилось множество недовольных пользователей, которые вынуждены будут вручную, по памяти настраивать новую версию программы, или, ещё хуже, старые настройки войдут в противоречие с новой версией, возникнет нетестированный баг. Предлагать пользователю «сбросить всё и установить по новой» — конечно, известный и безобразный способ решения таких проблем, с которым искушённые пользователи смирились. (В самом деле, что они могут сделать с производителем Фотошопа, когда пользы больше, чем неприятностей?) Лучше сразу заложить в проект технику разрешения конфликтов настроек при смене версий. Так были построены правила работы с версиями и настройками, которым мог бы следовать проектировщик программы.
Во второй части в следующей статье — реализация тестовой страницы на JS — готовый скрипт, в котором можно проверить работоспособность подхода. В скрипте настройки пользователя — произвольная многоуровневая структура, поэтому её можно портировать в различные проекты, в которых тоже задумались о необходимости корректной смены версий. Или, используя первую и вторую части, написать логику на другом языке. Усилия не пройдут даром и можно сэкономить на труде дизайнера по заготовке красивой таблички такого содержания:
О политике сохранения настроек программ при переходе к новым версиям.
Как уже упоминалось, смена версий программы — выход в новое измерение, от решения проблем с багами кодов в одной простыне программы к решению проблем её развития. Разработчики могут не сразу осознать специфику этой области, если долго работают над рядом других очень важных функций своей программы, а вопрос о смене версий на первом плане не стоит. Но когда-то он встаёт, и начинается эволюция.
Что было в эпоху динозавров: программа при установке новой версии начинает работать плохо или вообще не работает. Рекомендация: перед установкой новой программы удалите предыдущую версию (и очистить реестр и файлы папок от данных из старой версии программы). В особо тяжёлых случаях — не открывать данные, созданные в старой версии программой новой версии.
Другой древний вариант: при переходе к новой версии программа не спрашивает, какие были настройки — она ставит дефолтные, а пользователь устанавливает их вручную. Способ обхода проблемы с программой: иногда подменяют данные в ini-файлах, реестре (win detected), конфигурационных файлах, и этим спасают себя от части работы по настройке. Ещё варианты решения — хранить программу с образом виртуальной ОС, в которой она установлена; ставить redisributable пакет, в котором создали работающий настроенный образ программы, а затем его упаковали для повторной инсталляции; использовать тихую инсталляцию с параметрами настройки, предусмотренную в довольно многих серьёзных программах.
Случай более приспособленных форм жизни: программа начинает понимать старые настройки, оставшиеся от предыдущей версии. Что для этого нужно делать при создании программы?
Речь будет идти, то о программах, то о вебе и сайтах, потому что там и там предмет обсуждения один — пользовательский интерфейс, версии продуктов. Только входной поток иной. Для программ — это настройки и их наследование разными версиями, для веба — это страницы данных и их отображение разными браузерами. Иногда эти данные настолько похожи на настройки, что их начинают так называть, а веб-страницы или виджеты на них начинают называть приложениями.
Новые настройки
Хороши были времена, когда новая версия программы имела полностью новые настройки. Тогда вопроса наследования просто не стояло. Как в эпоху динозавров, новая версия просит всё-всё установить заново или пользоваться настройками по умолчанию. Но наследственность как базовое свойство развивающихся систем быстро изжила этот недостаток. От программ стала требоваться не только совместимость по данным, но и совместимость по настройкам. Сначала динозавры, а потом и куры начали требовать яиц, положив начало вопросу о том, что раньше появилось. Установил новую версию — не потерял заданные ранее настройки, скажем, размеров окна и шрифта. Но система настроек от версии к версии имеет свойство развиваться, поэтому приходится учитывать развиваемость программ, эволюцию жизненных форм. И ещё, программа может обновиться до какой угодно версии, даже до более старой, а значит, она должна корректно использовать не только любые предыдущие настройки, но и настройки в любом будущем формате!
Например, в системе тегов и атрибутов HTML, XML имеется правило: если встречается новый атрибут или тег, он игнорируется (его смысл неизвестен). Очевидно, и в системе настроек логично при появлении новых — игнорировать их. Так, в HTML-страницы, а ещё ранее — в настройки программ для разных версий той же программы ввели самое первое и простое правило совместимости.
Интерпретация имеющихся настроек
Однако, даже при неизменных настройках программа может (по ошибке) начать интерпретировать некоторую настройку иначе. Если программы — от разных производителей, которые от пользователя получают одинаковые данные, они могут интерпретировать данные в несколько разных смыслах. Например, у одних размер экрана учитывает первый монитор, у других — максимальное измерение из группы всех подключённых мониторов. Если снова обратиться к аналогии с HTML, то это в HTML известно как проблема совместимости представлений данных в браузерах. Даже одинаковые данные (таблицы, поля ввода) вызывают немного разное отображение результатов.
Есть проблема — взялись за её решение. Теперь есть «совместимое» представление, а есть строгое. При большом количестве версий браузеров количество строгих представлений растёт, а совместимых — растёт ещё быстрее (в квадрате), т.к. каждый браузер и версия будет иметь своё представление о совместимости.
Если бы мы приняли такой подход для совместимости настроек в программах, то надо было бы написать правила переноса настроек между версиями. Потому что установка версий не всегда происходит последовательно. Есть перескоки через версии, есть возвраты к прежним (например, по причине багов новых версий). Понятно, что этот процесс, даже если будет начат на уровне 2-3 версий программы, быстро наскучит разработчикам, и они начнут поддерживать только самые актуальные совместимости — переход от старой версии к новой.
Придерживание единой линии поведения — лучшее средство постройки Вавилонской башни.
Исчезновение старых настроек или некоторых их значений
Если в новой версии программы (или даже при новом запуске) перестали поддерживаться значения, установленные в настройках ранее, программа должна достойно на это отреагировать.
Например, в ту же эпоху динозавров, все (или многие) программы считали, что должны появляться в середине экрана и занимать примерно 50% ширины экрана. Если пользователь решит сменить положение и размер окна, было совсем не обязательно, что в следующий раз программа откроет окно на новом месте. Почему так?
Если программа запомнит положение и размеры окна, то в другой раз она может запросто оказаться за пределами экрана, если сменили монитор. Поддержка программы усложняется, а эволюция не всегда поддерживала сложные формы жизни. Увидеть монитор и окружение — это долгие процедуры, которые в неразвитом API создавали ад для разработчика, и они (или эти идеи у них) вымирали, даже не дав потомства — работающих в этом ключе программ. Программы могли быть крайне сложными, но иметь примитивный интерфейс, потому что каждое дополнительное действие над работающей программой могло дать серию багов и необходимость нового цикла тестирования.
Но шли годы, и с появлением более совершенных API и сред разработки у разработчиков прибавилось сил и уверенности в будущем.
Разумные альтернативы:
1) выбрать ближайшее похожее значение;
2) выбрать значение по умолчанию;
3) предложить выбор комплекса связанных значений, поскольку изменение параметра иногда требует изменения других.
Разумная программа реагирует на недопустимое для данной версии значение.
Смена семантики настройки
Это — наиболее сложный случай запутывания программой своих прежних настроек. Если контроль настроек базируется на передаче имени + значения каждой настройки, то искажение смысла (т.е. действий программы) значения приведёт к неконтролируемому пользователем изменению поведения программы.
Например, ранее 1 — это открывать на весь экран, теперь — в большом окне.
Изменилось понимание дефолтной настройки, а в файле настроек не хранятся дефолтные значения. Очевидно, что наличие таких изменений смысла приведёт к ошибкам и потребует исправления.
Таким образом, как браузеры стремятся белое называть белым (правда, не всегда, например фон страницы кастомизируется), так и программа стремится в настройках видеть то, что в них было заложено по смыслу. (Если некоторая переменная windowWidth указывала на ширину окна, нет смысла в новой версии ей указывать количество работников предприятия.)
Не все поступают так откровенно. Вот — (гипотетический) пример очень скрытного изменения смысла. До версии N ширину окна в ОС разработчики считали по внутренней границе окна, а с N-й решили, что правильнее считать вместе с бордюром. И тогда, если ранее пользователь устанавливал в прежних версиях ширину 100, в N-й он получит внутреннюю ширину (по прежнему смыслу) 98, 96 или другую, зависящую от настроек ОС. На месте разработчиков, при необходимости ввода другой ширины, для неё нужно выбрать другое название (имя, ключ в настройках). А прежнее за ненадобностью можно навсегда удалить (или оставить процедуру конвертирования, только для совместимости версий).
Смену семантики значения настройки нужно рассматривать как ошибку разработчика.
Смена дефолтного значения
Наиболее безобидный случай из приводящих к нарушениям работы.
К примеру, есть 2 чекбокса, оба выбраны. Первый чекбокс пользователь обнулил, второй его устроил (а если бы не был выбран, пользователь его бы выбрал, потому что хочет значений, 0, 1). В следующей версии производитель сбросил оба чекбокса. Пользователь в сохранённых настройках имеет только выбор первого: 0, Х, где Х означает предоставление выбора производителю. И имеет проблему, потому что в интерфейсе не было и нет возможности закрепить дефолтное значение второго чекбокса.
Результат — неодинаковая работа системы настроек для разных пользователей. Всё в порядке с пассивными и проблемы для активных, не могущих зафиксировать выбор дефолтной настройки. (Из этого следуют рекомендации профессионалов в смежных областях — CSS, например — учитывать действия атрибутов по умолчанию, прописывая их явно (CSS reset), чтобы в случае изменения политики умолчания в будущем не возникло иного понимания атрибутов.)
Выход в том, чтобы давать возможность фиксировать настройку, даже если она находится в значении по умолчанию. Другими словами, пользователь выразит явное желание установить это значение, а не пассивное согласие на настройки (рекомендацию) производителя. Следовательно, мы не можем, как это обычно делают, не дать возможность указать фиксацию настройки пользователя в том же значении, в котором она находится сейчас. (Это и был тот самый тонкий момент, сбой системы без фиксации дефолтных настроек, который заставил разобрать проблемы.)
В системах голосования, если кто-то уже проголосовал так же, как и пользователь, вовсе не значит, что пользователь не может так же проголосовать. Аналогично — и в настройках. «Облегчение» выбора настройки по умолчанию без возможности подтверждения, фиксации её в интерфейсе является «медвежьей услугой» и источником проблем. С фиксацией — соблюдается баланс рекомендации производителя и выбора пользователя.
В нашем случае с чекбоксами, нужно не просто давать изменять настройки, но и указывать фиксацию каждой. Для оптимальности процесса, если пользователь что-то изменил, это уже означает фиксацию. Зато, её теперь нужно смочь снять — вернуться к значению по умолчанию, опять же, для каждой настройки. Аналогично — вернуться к выбору другого авторитетного пользователя, если система будет поддерживать не один профиль настроек, а импорт других авторских настроек в том числе.
Далее, производитель делает смену настроек неспроста, поэтому полезно, чтобы о ней узнали, в ненавязчивом режиме. Если пользователь заинтересовался настройками, он должен увидеть то новое, что произошло с ними с момента его последнего захода в настройки. Если он зашёл первый раз, ничего не выделяется, всё ново. Если что-то менял или фиксировал ранее, а производитель изменил свою рекомендацию — пользователь увидит изменение. Разработчик обеспечивает указание на смену дефолтных настроек.
Вопрос плавно подходит к системе помощи, подсказок, информации и выбору рекомендаций. Место оповещения об изменении настроек — также лучшее место описания настроек вообще или подсказок по ним.
Следовательно, интерфейс выбора настройки обрастает 2 элементами: фиксацией/снятием фиксации и подсказкой как по истории изменений, так и описанием настройки вообще.
Примерно так (просто перечислены элементы управления):
[0] - чекбокс_1 (?) _fix_
[1] - чекбокс_2 (?) _unfix_
[__] - поле_3 (?) _fix_
В квадратных скобках — значение чекбокса; знаки вопроса — подсказки; цвет вопросов может указывать на то, что системе есть, что сказать об изменениях. Значки «fix/unfix» — ссылки на фиксацию дефолтов или возврат к ним. Если рекомендателей несколько, значки фиксации усложняются. Можно иметь в ряду настроек — указание о пользователях, выбравших эту настройку (сколько и с каким статусом), и среди них — значение настройки разработчика. С таким подходом настройки начинают напоминать оценки социальной сети. Что ж, может быть, это — один из вариантов коллективного использования программ.
Если механизма фиксации нет — имеем проблемы интерфейса и логики. Впрочем, в интерфейсах чекбоксов в ОС эту проблему решили перебором 3 состояний: 0, 1, default. В HTML её тоже решить можно применением disabled и циклическим перебором с помощью JS. Тогда, по крайней мере, для чекбоксов и радиокнопок не потребуется элемент fix/unfix.
Что, если работа новой версии не устроила?
В этом случае, очевидно, требуется откат к последней устраивающей версии. При этом некоторые данные могут остаться новые (обычно из предметной области), а настройки программы следует брать из старых настроек, но при этом часть новых настроек тоже может оказаться полезной и сохраняется как рекомендация для старых или для новой стабильной версии.
… Второй раз появилось слово «рекомендация». Это непривычно для настроек. Есть «дефолтное значение» как рекомендация разработчика, есть (бывает) сброс к дефолтным значениям, но нет рекомендаций. Есть импорт настроек, но тогда все настройки примут новые значения, и сравнивать можно лишь с тем, что было до смены. Рекомендация — это возможность попробовать изменить настройку или их группу и немедленно увидеть и сравнить результат.
Идея рекомендации и сравнения — очень мощная. (Так, кстати, работала и эволюция в эпоху динозавров.) Ряд сайтов магазинов старается максимально удобно представить товары для сравнения. Это — та же рекомендация выбора, как рекомендация установить некоторую настройку. Поэтому, если мы осилим показать в программе последствия рекомендации настройки, откроются перспективы сделать ещё очень многое и неожиданно полезное тем же инструментом.
Вернёмся к откату версии. Чтобы полноценно сделать откат, программе не следует безоговорочно забывать старые настройки. Лучше, если она помнит последний шаг смены версии программы. Если пользователь не успел опробовать все возможности прежних версий — пусть помнит и прежние шаги. Тогда он получит возможность выявления последней правильно работающей версии. Очевидно, это будет полезно для разработки и тестирования. В эпоху динозавров такого ещё не было…
Разумное ограничение интерфейса
Правда, не хотелось бы так усложнять интерфейс выбора настроек, чтобы оперировать с несколькими наборами сразу. Это хорошо для профессиональных тестировщиков и других разработчиков, но есть задача сделать массовый элемент управления, поэтому лучше, если он будет максимально простым и гибким. Обычно гибкость достигают экспортом-импортом настроек. С ним пользователь в отдельном месте вне программы может реализовать любую систему каталогизации и хранения версий, что опять-таки, сложно. В дополнение ко всесильной системе экспорта, хорошо бы иметь простую подсистему, не требующую прибегания к экспорту, насколько это будет возможно.
Попробуем хранить не 1 список настроек, а 1 или 2, относящихся, например, к текущей версии и к прежней. Или текущую версию и экспериментальную. Тогда в интерфейсе управления нам надо иметь всего одну кнопку (как в калькуляторах — кнопку «М» (память) ), чтобы переключаться между своими настройками и не путаться в них, не вводить имена и не строить «менеджер». Для всего, что больше — имеется к услугам экспорт. Это фундаментальное упрощение интерфейса, будем надеяться, позволит построить практичную модель и останется интуитивно понятной, как кнопка «М».
Отправная точка такой идеи — в том, что большинство действий, в том числе проверка новой версии и откат к старой, не требуют оперирования с более чем 2 списками настроек. Проверка чьей-то рекомендации с хранением одного «комплекта» своих настроек — тоже. Далее опишем техническую реализацию.
Хранить настройки — означает разместить их в localStorage, чтобы использовать в пределах браузера, независимо от перезагрузок. Кроме хранимых наборов, можно временно тестировать другие настройки, не затрагивая хранимых. Сохранение делается нажатием кнопки «Сохранить» или загрузкой новой версии программы. Откатиться можно без использования новых настроек (по умолчанию) или с использованием (указывается при откате). Перейти к произвольной другой версии — с выбором из тех же двух. Новые настройки выбираются, если номер версии — больше версии настроек нового комплекта. Если 2 комплекта одинаковы, всё равно считается, что их — два. Хранятся — как разности, поэтому, чем меньше различий, тем меньше занято места. Текущие настройки хранятся как разность между рекомендациями производителя и пользовательским выбором. Разность понимается как указание настройки в случае, если она выбрана пользователем (даже если совпадает с дефолтной) и неуказание, если иначе.
Есть ещё один технический момент, что в браузере наиболее просто реализовать чтение настроек в начале загрузки страницы, а затем считать их константами. Процесс смены настроек требует перезагрузки, поэтому какое-либо тестирование требует сохранения комплекта настроек, а для незабывания настроек пользователя нужно хранить 2 комплекта и использовать затем ту же кнопку «Вернуться» для восстановления.
Что можем делать с 2 списками настроек? Соберём полный список.
1. Перейти к новой версии программы и загрузить новые настройки из неё. Старые останутся для отката, а новые — для продолжения работы.
2. Если выполняется откат, во втором наборе сохраняются данные новых настроек, если их пользователь изменял. При повторном переходе к той же или более новой версии используется наработанное.
3. Сохранение другой рекомендации (импорт — сохранить) стирает данные для отката, чтобы настройки другой рекомендации могли исследоваться после перезапуска браузера.
4. Тестирование других настроек (настройки — тестировать — сохранить), которые можно продолжить после перезапуска браузера и после перезагрузки страницы.
Ничего не мешает тестировать настройки без их сохранения, но тогда мы ограничены пределами времени до перезагрузки страницы. Так можно протестировать самые простые и немедленно действующие настройки. Большинство настроек устанавливают свои значения в программе как раз в начале загрузки страницы, поэтому без сохранения в localStorage они не смогут быть протестированы. Механизм сохранения 2 комплектов настроек позволяет тестировать без таких ограничений, что создаёт хорошее дополнение к возможностям скриптов.
Почему такое внимание к количеству данных и их ограниченности?
Потому что, с одной стороны, есть стремление к простоте и понятности настроек. Чем меньше сущностей, тем лучше. Самое простое — иметь один комплект настроек — «мои». Для минимума манипуляций вводятся настройки «другие» и единственная кнопка переключения «Возврат». Запутаться сложно, а функций добавляет. Есть и другая сторона вопроса.
Казалось бы, что трудного в том, чтобы перенести настройки на сервер в базу данных, а там — работать с ними сколь угодно сложно: хранить для каждой версии, пересматривать историю? Тут подходим к паре других сущностей нашей задачи. Первая — предполагается хранение настроек независимо от авторизации на сервере. Без авторизации или для другой задачи — в качестве настроек юзерскрипта на клиенте, где авторизации тоже не предполагается. Вторая сущность постановки — хранение настроек в текстовом виде для экспорта предполагает наиболее компактную форму хранения, поэтому надо иметь наиболее короткую и редактируемую вручную строку. Поэтому есть 2 комплекта, и то, хранятся только разности настроек, чтобы данных было меньше.
Интерфейс переключения настроек
Этот раздел требует отдельной статьи и другого блога. То, что делали раньше, называется «Архитектура программы», но такого блога нет, есть только тег «Архитектура». Чтобы описание было законченным, опишем результаты кратко, словами, без иллюстраций, а подробности вынесем в другую статью. Получилось, как обычно в текстовых описаниях интерфейсов — скучно, трудночитаемо и предназначено только тем, кто подобным занимался и хочет сравнить результаты.
В начале, без особых действий пользователь не получает никаких дополнительных элементов в интерфейсе нигде, до тех пор, пока он не начнёт пробовать изменить настройки. Нет даже (или не активизирована) кнопки «Экспорт», потому что экспорт пустой строки — это, по умолчанию, согласие с настройками производителя.
Есть кнопка (или поле ввода) "Импорт", по которой через поле ввода загружаем строку JSON или нечто подобное для смены настроек. Немедленно видим отражение этих настроек в интерфейсе настроек, название рекомендателя, версию настроек + версию программы, для которой они были сделаны. Нет ничего необычного, если в другую версию программы (старую или новую) загрузим настройки произвольной версии. Сработают те, ключи которой найдутся в текущей версии программы. Появляются призывные кнопки "Протестировать" и "Отмена". Вторая восстанавливает просмотр своих настроек и ничего не делает, а первая сохраняет как актуальную — загруженную импортированную версию, а как резерв — текущую, стирая при этом любую другую резервную версию, если она была (одноуровневый стек).
Наличие сохранённой резервной версии отмечается в панели настроек. Поэтому, как только нажмём «Протестировать» — на кнопке "Вернуться", которая придёт на смену, появится информация о сохранённой версии пользователя (её может не быть, если настроек пользователя не было, но режим «тест» всё равно включается). В интерфейсе страниц появляется постоянно висящая ссылка на блок настроек"тест" (как напоминание о пришедшей почте).
При нажатии на «Вернуться» получаем возврат настроек для программы (обычно приводятся в действие после перезагрузки страницы), а в панели настроек — настройки для теста и кнопки «Протестировать»-«Отмена» — прежний шаг операции.
Если загружается новая версия скрипта (программы), что отмечается по анализу localStorage, она действует на состояние настроек аналогично, если они были когда-то изменены. Создаётся копия старых настроек и… аналогично «тест», появляется нотификатор "новая версия". Если этого не сделать, изменение поведения программы вызовет недоумение пользователя. Здесь возникает вопрос, показывать ли эту кнопку всем или только интересующимся. Скорее всего, второе, а для всех — нотификатор прячется в панель настроек.
Если смена версии происходит многократно, ничего особо не меняется, только значение новой версии скрипта (и новые настройки). Если пользователь изменил настройки для новой версии, появляется кнопка "Сохранить", по клику на которой появляется 2 группы настроек: к новой версии и к старой. «Вернуться» вызовет ротацию версий: слово «Вернуться» сменится на "Применить новые", чтобы снова переключиться на новые настройки, как в случае «Протестировать». Чтобы вернуться к старой программе, нужно нажать появившуюся кнопку "Откат", который произойдёт на версию, номер которой запомнен при последней правке старых настроек. (Этот механизм требует доступа к любой выпущенной версии.) Для переключения к другим версиям — спадающий список номеров.
Если пользователь изменил комплект настроек ещё раз, когда 2 комплекта сохранены, но имеется 3-я версия программы, нового сохранения в стеке не будет, изменится только текущая группа (после нажатия «Сохранить»).
Кнопка "Подтвердить настройки" — чтобы отказаться от хранения запомненного состояния — отката, оставшись на новой версии (значок «новая версия» удаляется). Если состояние одно, кнопка называется «Стереть настройки» или "Сброс", и память очищается от настроек, используются настройки по умолчанию. «Сброс» защищён от случайного нажатия.
Учитывая ограничение числа комплектов настроек двумя, любое действие тестирования сотрёт настройки отката и не восстановит их по окончанию теста. Чтобы их не потерять, пользуются кнопкой экспорта, которая активизируется при наличии какой-либо пользовательской настройки.
Перечислим ещё раз список кнопок и нотификаторов и их группировку:
* Импорт, Экспорт — для внесения-извлечения настроек в виде строки;
* Протестировать, Отмена — применение настроек, отмена показа импорта в настройках;
* «тест» — нотификатор, подсказывающий, что настройки находятся в режиме теста;
* Вернуться — восстановление своих настроек, выход из теста;
* «новая версия» — нотификатор, говорящий о загрузке новой версии (в панели настроек);
* Откат — аналог «Вернуться» для отката к прежней версии;
* комбо или список версий — к какой версии будет откат;
* Сохранить, Отмена — подтверждение смены настроек от пользователя; аналогично «Протестировать», но без нотификации;
* Подтвердить настройки (вместо «Вернуться») или Сброс — удаление хранимых настроек, применение текущих (или умолчательных), снятие нотификатора «новая версия»;
* Применить новые — после отката — перейти обратно к новой версии настроек.
В следующей серии попробуем увидеть всё это вживую, но, вполне возможно, что в скрипте это ещё не будет работать, а писать статью с разрисовкой интерфейса будет никому не нужно — программирование идёт без макетов. Тогда дождёмся публичного скрипта с описанными механизмами в качестве демонстрации.
Итоги
Получен интерфейс работы с настройками без технологических изъянов, которые могли быть без учёта развития программы при смене версий. Введено понятие «рекомендация» и предусмотрена возможность работы с несколькими рекомендациями и с форматом настроек, совместимым по версии программы в любую сторону — работающим как с новыми версиями (для апгрейдов), так и со старыми (для откатов).