Структуры данных для хранения сложных опционных отношений в графическом интерфейсе - PullRequest
2 голосов
/ 28 января 2010

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

Мой вопрос такой:

У меня есть графический интерфейс с множеством включенных / отключенных опций, флажки.

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

Я реализовал это очень плохо, он работает, но есть множество мест, которые примерно выглядят так:

if (checkboxA.isEnabled() && checkboxB.isSelected()) 
{
   //enable/disable a bunch of checkboxs
   //select/unselect a bunch of checkboxs
}

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

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

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

Существует ли структура данных, которую программисты GUI порекомендовали бы для работы с такой ситуацией? Я предполагаю, что это проблема, которая была элегантно решена раньше.

Спасибо за ваши ответы.

Ответы [ 6 ]

1 голос
/ 31 января 2010

Важная экономия кода и здравого смысла, которую вы ищете здесь: декларативный подход и DRY (не повторяйте себя).

[ Пример для следующего: допустим, запрещено включать все 3 флажка A, B и C. ]

Брайан Бэтчелдер дал первый шаг к наведению порядка: напишите единственное правило для правильности каждого флажка:

if(B.isSelected() && C.isSelected()) {
   A.forceOff();  // making methods up - disable & unselected
} else {
   A.enable();
}
// similar rules for B and C...

// similar code for other relationships...

и переоценивайте это всякий раз, когда что-то меняется. Это лучше, чем рассеяние изменений состояния A во многих местах (когда B изменяется, когда C изменяется).

Но у нас все еще есть дублирование: единое концептуальное правило, для которого комбинации A, B, C являются законными, было разбито на 3 правила для случаев, когда вы можете разрешить бесплатные изменения A, B и C. В идеале вы должны написать только это:

bool validate() {
    if(A.isSelected() && B.isSelected() && C.isSelected()) {
        return false;
    }
    // other relationships...
}

и все флажки включения / форсирования выводятся из этого автоматически!

Можете ли вы сделать это из одного правила validate ()? Я думаю, что вы можете! Вы моделируете возможные изменения - validate () вернет true, если A включен? выкл? Если оба варианта возможны, оставьте A включенным; если возможно только одно состояние A, отключите его и установите его значение; если это невозможно - сама ситуация является незаконной. Повторите вышеприведенное моделирование для A = другие флажки ...

Что-то внутри меня жаждет потребовать симуляции всех возможных комбинаций изменений. Подумайте о таких ситуациях, как «A еще не должен отключать B, потому что, хотя в настоящее время он запрещен, при включенном C включение B приведет к выключению C, а с этим B допустимо» ... Проблема в том, что на этом пути лежит полное безумие и непредсказуемый интерфейс поведение. Я считаю, что моделирование изменений только одного виджета за раз относительно текущего состояния - это то, что нужно, но я слишком ленив, чтобы доказать это сейчас. Так что принимайте этот подход с долей скептицизма.


Я также должен сказать, что все это звучит в лучшем случае сбивает с толку пользователя! Несколько случайных идей, которые могут (?) Привести вас к более удобному дизайну GUI (или, по крайней мере, уменьшить боль):

  • Используйте структуру GUI, где это возможно!
    • Группировка виджетов, зависящих от общего условия.
    • Используйте переключатели над флажками и выпадающими выборками, где это возможно.
      Радиокнопки можно отключить индивидуально, что улучшает обратную связь.
    • Используйте переключатели, чтобы сгладить комбинации: вместо флажков "A" и "B", которые не могут быть включены одновременно, предложите переключатели "A" / "B" / "none".
  • Список ограничений совместимости в графическом интерфейсе / подсказках!
    • Автоматически генерировать всплывающие подсказки для отключенных виджетов, объясняя, какое правило заставило их?
      Это на самом деле легко сделать.
  • Рассмотрите возможность разрешения противоречий, но перечислите нарушенные правила в области состояния, требуя от пользователя разрешения, прежде чем он сможет нажать OK.
  • Реализовать отмену (& redo?), Чтобы отключение виджетов не было разрушительным?
    • Помните назначенное пользователем состояние флажков при их отключении, восстанавливать, когда они становятся включенными? [Но остерегайтесь изменения вещей без замечаний пользователя!]
0 голосов
/ 07 марта 2010

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

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

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

Итак, я сделал сборкупростая система правил / проверки.

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

Каждая строка определяет допустимую конфигурациюдля всех флажков.

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

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

0 голосов
/ 28 января 2010

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

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

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

0 голосов
/ 28 января 2010

Я бы подумал об этом подобно тому, как обычно работают правила валидации.

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

0 голосов
/ 28 января 2010

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

Как правило, это гораздо лучше, чем пытаться: 1. кодировать лабиринт логики «если тогда»; и 2. изменение этого лабиринта позже, когда изменятся бизнес-правила.

Один, чтобы проверить на Java: JBoss Drools

0 голосов
/ 28 января 2010

Мне приходилось работать с похожим графическим интерфейсом и столкнулся с той же проблемой. Я никогда не находил хорошую структуру данных, поэтому буду с интересом наблюдать за другими ответами на этот вопрос. Это становится особенно сложным, когда вы имеете дело с несколькими различными типами элементов управления (поля со списком, представления списка, флажки, панели и т. Д.). Что я нашел полезным, так это использование описательных имен для ваших элементов управления, чтобы было очень ясно, когда вы смотрите на код, для чего предназначен или предназначен каждый элемент управления. Кроме того, организация кода так, что элементы управления, которые влияют друг на друга, сгруппированы вместе. При создании обновлений не просто добавляйте какой-нибудь код внизу функции, который влияет на что-то еще, что рассматривалось ранее в функции. Потратьте время, чтобы поместить новый код в нужное место.

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