处理 GUI 应用程序中的复杂规则(C++ 或 C#)
Posted
技术标签:
【中文标题】处理 GUI 应用程序中的复杂规则(C++ 或 C#)【英文标题】:Handling Complex Rules in GUI applications (C++ or C#) 【发布时间】:2011-01-31 17:23:10 【问题描述】:我正在处理一个对话框,其中必须满足几个规则才能启用“确定”按钮。
当前页面上的任何操作,例如输入数据或从下拉列表中选择一个项目(除其他外)都会调用一个名为 ProcessEvent() 的函数 - 该函数处理所有逻辑并启用或禁用“确定”按钮。
我的问题是我发现很难让规则简明易懂。
一些规则可以被对话框上的另一个动作否定,我现在已经结束了 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;
【问题讨论】:
【参考方案1】:我会将您的 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);
Condition1() 可能在哪里:
bool Condition1()
return (IsDlgButtonChecked(IDC_USEDBNAME)
&& !IsDlgButtonChecked(IDC_MAXDBNAME));
等等。
这里发生的是 enableNext 变量以 true 开头。然后,您所做的每个 &= 意味着如果任何 ConditionX() 函数返回 false,则 enableNext 将最终为 false。只有当所有条件都为真时,它才会在最后为真。
【讨论】:
看起来不错。现在我只需要弄清楚 "&=" 到底是做什么的。 啊是的。 &= 是一个逻辑快捷操作符。 enableNext &= Condition1() 与 enableNext = enableNext && Condition() 相同 好吧,从技术上讲,它是 enableNext = enableNext & Condition1(),但对于布尔值来说,它实际上是一回事。请参阅编辑后发布。 小提示:可能需要注意没有 &&= 运算符,所以 Condition1() 总是用 &= 调用。如果函数没有副作用,效果很好,但如果它改变状态,你可能会得到一些意想不到的东西。【参考方案2】:这个问题可以通过监听器的概念来解决。
您可以让每个 GUI 组件都有一个 isEnabled()
方法,该方法根据某些条件检查其条件。当调用任何改变任何组件状态的操作时,每个 GUI 组件上都会调用 isEnabled()
。
这样你可以有以下声明:
bool CheckBoxComponent::isValid()
return isNameFilled() && isEmailChecked();
bool OkButton::canSend()
return checkBoxName->isValid() && isEmailChecked();
然后,在创建您的 GUI 组件时,您可以让它们中的每一个通过侦听器相互连接。
这样,您就可以为每个组件所属的每个组件制定规则,并且您没有大量的 if 语句。
【讨论】:
似乎确实适合Observer
模式。处理该事件,然后使用对象的notify
方法通知所有为它注册的Observers
。【参考方案3】:
尝试将规则表述为状态机可能会有所帮助,但是否可行取决于它们的性质。在这种方法中,每当用户填写对话框中的某些字段,或选中复选框或其他任何内容时,您都会相应地更新您的状态机的状态。如果你有,你可以使用Boost.Statechart 来实现它。
【讨论】:
【参考方案4】:在这种情况下,我倾向于通过(例如)默认启用按钮来使其尽可能简单,如果设置了(或未设置)任何其他条件,则禁用它;这限制了“if”条件下的不同情况与“else”。
【讨论】:
【参考方案5】:将您的条件重述为正确的布尔语句,正确缩进所有条件并添加一些 cmets。恕我直言,您不应该在一次性方法中隐藏真正的检查。如果您想对代码进行注释,请对其进行注释,但不要为此创建方法,它只会混淆事物并且您的条件不会变得更简单:
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
总是被调用。
【讨论】:
好点。我建议的一件事是仍然使用本地 enableNext 和单独的 &=,就像在我的示例中一样,但条件是内联的。通过这种方式在调试器中你可以知道是哪个条件导致它被禁用。 这有时对调试很有用。不过要小心 &=,它不是逻辑运算符,而是按位运算符。【参考方案6】:虽然解决方案可能比您想要的要“重”一些,但您可能需要查看 Adobe 的 Adam and Eve 库。 Eve 处理小部件布局,Adam 获取一组关于小部件逻辑的语句,并将它们放在一个控制器中,该控制器基于该逻辑启用和禁用小部件,以及处理初始化并将结果放入适当的变量中(例如, 当用户点击“确定”时)。
【讨论】:
链接已过期。新的:stlab.adobe.com/group__asl__overview.html @Xeverous:谢谢。以上是关于处理 GUI 应用程序中的复杂规则(C++ 或 C#)的主要内容,如果未能解决你的问题,请参考以下文章