Обработка сложных правил в приложениях с графическим интерфейсом (C ++ или C #) - PullRequest
4 голосов
/ 25 марта 2010

Я работаю над диалоговым окном, в котором необходимо выполнить несколько правил, прежде чем кнопка ОК будет включена.

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

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

Некоторые из правил могут быть отменены другим действием в диалоговом окне, и теперь у меня закончились операторы if else повсеместно или которые трудно читать, следовать и расширять.

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

bool CWorkstation::ProcessEvent(void)
    {   
        UpdateData();

        CharCount = GetDlgItemInt(IDC_CharCount, NULL, FALSE); //get latest

        if ( IsDlgButtonChecked(IDC_USEDBNAME))
            {   
                if (!IsDlgButtonChecked(IDC_MAXDBNAME))
                    {
                        EnableNext(TRUE);
                    }
            }

        if (IsDlgButtonChecked(IDC_MAXDBNAME) && CharCount)
            {                   
                if (IsDlgButtonChecked(IDC_USEXMLNAME))
                    {
                        if ( PrefixName.IsEmpty() ) 
                            {
                                EnableNext(FALSE);
                            }
                        else
                            {
                                EnableNext(TRUE);
                            }
            }



            }   


        if (IsDlgButtonChecked(IDC_USEXMLNAME) && PrefixName.GetLength() > 1)
            {
                EnableNext(TRUE);
            }



        if  ( IsDlgButtonChecked(IDC_WSAUTONAME) || IsDlgButtonChecked(IDC_RENAMEIFDUP))
            {

            // TRACE("IDC_WSAUTONAME is Checked\n");

            if ( IsDlgButtonChecked(IDC_USEXMLNAME) && PrefixName.GetLength() > 1 ) 

                {   


                if ( IsDlgButtonChecked(IDC_IDC_USESHORTNAME) ) 

                    {

                    EnableNext(TRUE);
                    }

                else if ( IsDlgButtonChecked(IDC_USELONGNAME) )

                    {

                    EnableNext(TRUE);

                    }

                else

                    {
                    EnableNext(FALSE);
                    }



                }


            if ( !IsDlgButtonChecked(IDC_USEPREFIX) )

                {


                if ( IsDlgButtonChecked(IDC_IDC_USESHORTNAME) ||  IsDlgButtonChecked(IDC_USELONGNAME) )

                    {
                    EnableNext(TRUE);
                    }

                }


            return false;


            }

        } 

Ответы [ 6 ]

6 голосов
/ 25 марта 2010

Я бы разделил ваши операторы if / else на несколько функций и сделал бы & = для параметра, который вы отправляете EnableNext. Вы должны вызывать EnableNext только один раз.

Так, например:

// in CWorkStation::ProcessEvent
bool enableNext = true; // start with true

enableNext &= Condition1(); // of course pick better names than Condition1
enableNext &= Condition2(); // this is just for an example

EnableNext(enableNext);

Где Условие1 () может быть:

bool Condition1()
{
    return (IsDlgButtonChecked(IDC_USEDBNAME) 
         && !IsDlgButtonChecked(IDC_MAXDBNAME));
}

и т. Д.

Здесь происходит то, что переменная enableNext начинается с true. Затем каждая & =, которую вы делаете, означает, что если любая из функций ConditionX () вернет false, enableNext в конечном итоге станет false. Это будет верно только в конце, если ВСЕ условия выполняются.

1 голос
/ 25 марта 2010

Эту проблему можно решить с помощью концепции слушателей.

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

Таким образом, вы можете иметь следующие объявления:

bool CheckBoxComponent::isValid() {
   return isNameFilled() && isEmailChecked();
}

bool OkButton::canSend() {
   return checkBoxName->isValid() && isEmailChecked();
}

Затем при создании компонентов GUI вы заставляете каждый из них соединяться друг с другом через слушатель.

Таким образом, у вас есть правила для каждого компонента, к которому они относятся, и у вас нет тонны операторов if.

0 голосов
/ 25 марта 2010

Хотя это решение может быть немного «тяжелее», чем вы хотели бы, вы можете взглянуть на библиотеки Adam и Eve от Adobe. Ева занимается макетом виджетов, а Адам принимает набор утверждений о логике виджетов и объединяет их в контроллер, который включает и отключает виджеты на основе этой логики, а также обрабатывает инициализацию и помещает результаты в соответствующие переменные (например, , когда пользователь нажимает «Ок»).

0 голосов
/ 25 марта 2010

Переформулируйте ваше условие как правильное логическое выражение, правильно сделайте отступ для всех условий и добавьте несколько комментариев. ИМХО, вы не должны скрывать реальные проверки в одноразовых методах. Если вы хотите комментировать код, комментируйте его, но не создавайте методы для этой цели, он только запутывает вещи, и ваши условия не упрощаются:

EnableNext( 
        // condition 1 
        IsDlgButtonChecked(IDC_USEDBNAME) && !IsDlgButtonChecked(IDC_MAXDBNAME)
        // condition 2
    ||  IsDlgButtonChecked(IDC_MAXDBNAME) && CharCount 
        && IsDlgButtonChecked(IDC_USEXMLNAME) && !PrefixName.IsEmpty()
        // condition 3
    ||  IsDlgButtonChecked(IDC_USEXMLNAME) && PrefixName.GetLength() > 1
        // and so on
)

Таким образом, сразу становится очевидным, что вы, кажется, проверяете одно и то же условие дважды USEXMLNAME && !PrefixName().IsEmpty(). Теперь также очевидно, что EnableNext всегда вызывается.

0 голосов
/ 25 марта 2010

В подобных случаях я стремлюсь сделать это как можно более простым, например, включив кнопку по умолчанию, и, если задано (или нет) какое-либо другое условие, отключите его; это ограничивает различные случаи в условиях «если» с «еще».

0 голосов
/ 25 марта 2010

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

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