0

在实现 GUI 时,我经常遇到一个难题。假设我有一个简单的小部件层次结构,主窗口有几个子小部件。子小部件应该自己处理事件(例如,来自用户输入),还是将处理委托给它们的父小部件?这是一个简单的例子:当点击它的关闭按钮时,一个窗口应该关闭。谁关上窗户?窗口是否对按钮事件做出反应并自行关闭?还是按钮调用窗口上的 Close 方法?我正在使用的框架强烈暗示首选方式是让子小部件向其父小部件触发事件,父小部件应按其认为合适的方式处理它们,因此窗口将像这样关闭(在 C++-ish 伪代码中):

void ChildWidget::OnClick()
{
    GetParentWidget()->OnEvent(CreateClickEvent(*this));
}

void ParentWidget::OnEvent(Event& _event)
{
    if (_event.Type() == EventTypeClick && _event.Id() == "close_button")
    {
        Close();
    }
    else if ...
        ...
}

但是,恐怕它违反了单一职责原则,导致父小部件做太多工作。相反,我可以为子小部件提供处理事件所需的对象,例如:

class CloseButtonWidget : public Widget
{
public:
    CloseButtonWidget(Widget& widget_to_close)
    : WidgetToClose_(&widget_to_close)
    {}

    void OnClick()
    {
        WidgetToClose->Close();
    }       

private:
    Widget* WidgetToClose_;
};

我可以看到的缺点是,在许多情况下,我必须为这样的自处理小部件创建一个新类,但是,无论如何,我经常必须为子小部件创建新类,所以这通常不是什么大问题.

我能想到的另一种方法是给子小部件一个回调,例如:

auto close_widget = new Widget();
// Suppose parent_widget is in scope
auto handler = CreateHandler(EventTypeClick, [parent_widget](){ parent_widget->Close(); }); 
close_widget->SetHandler(handler);

所以问题是 - 处理来自子小部件的事件的最佳位置是什么?上述方式的优缺点是什么?也许还有其他更好的方法可以做到这一点?我会特别有兴趣听到“父小部件处理一切”场景的任何优点,因为我发现很难看到任何优点。我想澄清一下,我想要一个以更“哲学”的方式解决问题的答案(比如更好的设计),保持我使用的框架的首选方式和怪癖不是很好重要的。

4

1 回答 1

0

IMPO第一种方法是瓶颈。操作系统/框架做了很多工作来为您提供特定于小部件的事件,以避免您必须执行“获取”过程。您通过单个事件处理程序处理每个事件?听起来像是荒谬的浪费时间。

如果您想以相同的方式处理一组事件(或从一组小部件引发的相同事件),请使用相同的事件处理程序。

例如,一个类似战舰的游戏:你有一个按钮网格,你以相同的方式处理网格中所有按钮的点击事件(一个按钮和另一个按钮的唯一区别是它的坐标)。

如果您必须在每个小部件事件(或同一小部件​​的不同事件)中执行不同的操作,请使用其自己的处理程序处理特定于小部件的事件。

正如您所注意到的,特定处理程序的问题在于它们的实现。让你重复代码。但是认为你不是在 Java 中!您拥有一流的函数(通过原始函数指针或函子),而不是适合 20 行的伪函数委托(也称为内部类)来编写单行操作。你在 C++ 中,你有模板、lambda、仿函数和其他有用的工具来编写特定的处理程序,只需要一行简单而清晰的代码:)

如您所见,我更喜欢每次可能/简单时都使用您的第三种方式。

于 2013-06-12T16:25:44.827 回答