Handling complex rules in graphical applications (C ++ or C #)

Im working on a dialog box in which several rules must be followed before the OK button is turned on.

Currently, any action on the page, such as entering data or selecting an item from the drop-down list (among other things) calls one function called ProcessEvent () - this function processes all the logic and enables or disables the OK button.

My problem is that I find it difficult to make the rules concise and understandable.

Some of the rules can be nullified by another action in the dialog box, and now I have finished working with if else instructions all over the place or which are difficult to read and follow and expand.

The code below simplifies the problem, but demonstrates it well. How best to deal with this problem (if possible)

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; } } 
+4
source share
6 answers

I would split your if / else statements into several functions and do & = for the parameter that you send to EnableNext. You must call EnableNext only once.

So for example:

 // 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); 

Where Condition1 () may be:

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

Etc.

What happens here is that the enableNext variable starts with true. Then, each of these elements means that if any of the ConditionX () functions returns false, enableNext will be false. This will only be true at the end if ALL conditions are true.

+6
source

This problem can be solved using the concept of listeners.

You can have each of your GUI components have an isEnabled() method that checks its conditions based on some conditions. isEnabled() is called for each component of the GUI when any action that changes the state of any component is called.

Thus, you can have the following declarations:

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

Then, when creating GUI components, each of them connects to each other through a listener.

Thus, you have rules for each component in which they are located, and you do not have three if statements.

+1
source

This may help to try to formulate the rules as a state machine, but if practical, it depends on their nature. In this approach, when a user fills in a field in a dialog box or checks a checkbox or something else, you update the status of your satellite machine accordingly. If you have this, you can use Boost.Statechart to implement it.

0
source

In such cases, I try to make it as simple as possible (for example) by letting the button default, and if any other condition is set (or not), disable it; this limits the various cases in an if condition with an else.

0
source

Repeat the condition as the correct logical expression, cancel all conditions correctly and add comments. IMHO, you should not hide real checks in one-time methods. If you want to comment on the code, comment on it, but do not create methods for this purpose, it only confuses things, and your conditions do not become easier:

 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 ) 

Thus, it becomes apparent that you seem to double check the same condition USEXMLNAME && !PrefixName().IsEmpty() . Now it is also obvious that EnableNext always invoked.

0
source

Although this may be a bit “harder” than you would like, you can look at the Adobe Adam and Eve libraries. Eva deals with widget layouts, and Adam takes a set of statements about widget logic and combines them into a controller that turns widgets on and off based on this logic, also processes initialization and puts the results in the correct variables (for example, when the user clicks "Ok").

0
source

Source: https://habr.com/ru/post/1305093/


All Articles