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