2013-06-12 3 views
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); 

Так что вопрос - что это лучшее место для обработки событий, поступающих от дочерних виджетов? Каковы преимущества и недостатки способов, описанных выше? Может быть, есть и другие, лучшие способы сделать это? Мне было бы особенно интересно узнать о каких-либо плюсах для сценария «родительский виджет справляется со всем», потому что мне трудно его увидеть. Я хотел бы пояснить, что мне нужен ответ, который будет решать вопрос более «философским» способом (например, что будет лучше), соблюдая предпочтительные способы и причуды используемой структуры, очень важно.

+0

В итоге я перешел на обратный путь.Мне пришлось написать кучу вспомогательных классов, чтобы компенсировать нашу структуру, и теперь жизнь кажется намного проще. – user2478832

ответ

0

IMPO первый метод является узким местом. OS/framework выполняет много ob-задание, чтобы предоставить вам специфичные для виджета события, чтобы избежать необходимости выполнять процесс «выборки». И вы обрабатываете каждое событие через один обработчик событий? Звучит как смехотворная трата времени.

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

Например, игра, подобная линкору: у вас есть сетка кнопок, и вы обрабатываете событие щелчка всех кнопок сетки таким же образом (единственная разница между одной кнопкой и другими - это ее координаты).

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

Как вы заметили, проблема с конкретными обработчиками - это их реализация. Заставляет вас перегружать код. Но думайте, что вы не на Java! У вас есть первоклассные функции (через raw-function-указатели или функторы), а не псевдофункции-делегаты (внутренние классы Aka), которые подходят для 20 строк для написания однострочной операции. Вы находитесь на C++, у вас есть шаблоны, lambdas, функторы и другие полезные инструменты для написания конкретных обработчиков только с одной простой и четкой строкой кода.

Как вы можете видеть, я предпочитаю использовать ваш 3-й способ каждый раз Thats возможно/простой.

Смежные вопросы