Оправдано ли использование пустого базового класса в этом примере?

Я пишу Window класс, который распространяет различные типы событий, перечисленных в

enum Event {WINDOW_ClOSE=0x1, WINDOW_REDRAW=0x2, MOUSE_MOVE=0x4, ...};

к объектам, которые зарегистрировались для уведомления с окном. Для каждого типа события у меня есть абстрактный класс, который любой объект должен расширять, чтобы разрешить уведомление. Чтобы реагировать, скажем, на MOUSE_MOVE событие, мой объект будет наследоваться от MouseMoveListener, который имеет process_mouse_move_event() метод, который вызывается Window, Прослушивание многих событий может быть объединено путем расширения нескольких этих классов, которые все наследуются от EventListener Базовый класс. Чтобы зарегистрировать объект, я бы позвонил

void Window::register(EventListener* object, int EventTypes)
{
if(EventTypes&WINDOW_CLOSE)
/* dynamic_cast object to WindowCloseListener*, add to internal list of all
WindowCloseListeners if the cast works, else raise error */

if(EventTypes&MOUSE_MOVE)
/* dynamic_cast object to MouseMoveListener*, add to internal list of all
MouseMoveListeners if the cast works, else raise error */

...
}

Это прекрасно работает, но моя хватка в том, что EventListener является полностью пустой, и это кажется мне вонючим кодом. Я знаю, что мог избежать этого, удалив EventListener в целом и имея отдельный Window::register для каждого типа событий, но я чувствую, что это может взорвать мой интерфейс без необходимости (особенно потому, что методы, кроме register может возникнуть с той же проблемой). Поэтому я думаю, что я ищу ответы, которые либо говорят:

  • «Вы можете продолжать делать это так, как вы делаете, потому что …» или

  • «Ввести отдельный Window::register методы в любом случае, потому что … «или, конечно,

  • «Вы все делаете неправильно, вы должны …».

РЕДАКТИРОВАТЬ:

По ссылке в комментарии Igors: То, что я делаю выше, работает, только если есть хотя бы один виртуальный член в EventListener например, виртуальный деструктор, поэтому класс технически не полностью пуст.

РЕДАКТИРОВАТЬ 2:

Я преждевременно принял решение н.м. как один из типов «я все делаю неправильно». Однако это второй тип. Даже если я могу позвонить EventListener->register(Window&) полиморфно, Window необходимо реализовать интерфейс с высокой избыточностью (с точки зрения заявленных методов), который позволяет EventListeners зарегистрироваться для выборочного уведомления. Это эквивалентно моему альтернативному решению, описанному выше, только с дополнительным введением EventListener класс без уважительной причины. В заключение, канонический ответ выглядит так:

Не делай dynamic_cast + пустой базовый класс, просто чтобы не объявлять много похожих функций, это повредит вам при поддержке кода позже. Напишите много функций.

РЕДАКТИРОВАТЬ 3:

Я нашел решение (с использованием шаблонов), которое меня устраивает. Он больше не использует пустой базовый класс и не демонстрирует проблему обслуживания, указанную в n.m.

0

Решение

object->registerWindow (this, EventTypes);

Конечно, вам нужно реализовать registerWindow для всех EventListener наследники. Пусть они проверяют типы событий, которые имеют отношение к их.

ОБНОВИТЬ

Если это означает, что вам нужно переделать свой код, то вам нужно переделать свой код. Почему это так? Так как dynamic_cast является не правильный способ сделать типы включения. Это неправильный способ, потому что каждый раз, когда вы добавляете класс в вашу иерархию, вам нужно перейти и, возможно, обновить все переключает динамическое приведение в ваш старый код. Это становится очень запутанным и неосуществимым очень быстро, и это как раз та причина, по которой были изобретены виртуальные функции.

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

2

Другие решения

Это то, что я в итоге сделал:

template <int EventType> void register_(EventListener<EventType> Listener)
{
// do stuff with Listener, using more templates
};

Оказалось, что статический полиморфизм лучше подходит для моих нужд — я просто хотел избежать написания

register_mouse_motion_event(...)
register_keyboard_event(...)

и так далее. Этот подход также хорошо устраняет необходимость в пустом базовом классе.

0

По вопросам рекламы ammmcru@yandex.ru
Adblock
detector