Как избежать dynamic_cast в дизайне GUI

Я проектирую графический интерфейс, и у меня есть проблема, потому что я не могу избежать dynamic_casting.

Мои занятия:

  class Widget; //base class for all widgets

class Container //contains widgets
{
std::map<std::string, Widget*> m_widgets;
public:
template <class T> T* get(const std::string &name)
{
return dynamic_cast<T*>(m_widgets.at(name)); //I need casting here
}
}

Как я могу избежать динамического приведения? Я не могу позволить себе иметь контейнер для каждого типа виджета, потому что мой графический интерфейс должен работать с пользовательскими виджетами. Кроме того, у меня должен быть контейнер для каждого виджета, чтобы пользователю не приходилось хранить виджеты самостоятельно.

Зачем мне кастинг?

 class TextBox : public Widget
{
public:
std::string getText(); //I can't have it in Widget class, because it's object-specific
//also, my gui must work with user-defined widgets so I can't provide
//empty virtual functions for everything in Widget
}

1

Решение

Чтобы расширить мой комментарий …

Исходя из вашего кода, я предполагаю, что вы намереваетесь указать имя строки для каждого дочернего виджета, который я добавляю в Container, а затем, чтобы получить доступ к дочернему виджету по имени. Таким образом, вы ожидаете, что я напишу что-то вроде этого:

class LoginController {

Container *container;
static const char *kUsernameKey = "username";
static const char *kPasswordKey = "password";

public:

LoginController() :
container(new Container())
{
container->addChild(kUsernameKey, new TextBox());
container->addChild(kPasswordKey, new TextBox());
container->addChild("button", new Button("Log In"));
container->get<Button>("button")->setAction([](){
this->login();
})
}

void login() {
string username = container->get<TextBox>(kUsernameKey)->getText();
string password = container->get<TextBox>(kPasswordKey)->getText();
sendLoginRequest(username, password);
}

};

Проектирование Container этот способ выполняет поиск и проверку типов во время выполнения, но эти поиск и проверки типов могут выполняться во время компиляции.

Вместо этого спроектируйте API так, чтобы я держал свои собственные, специально напечатанные ссылки на потомков в моих собственных переменных. Поиск детей просто становится с помощью переменных, и нет необходимости в приведении. Поиск и проверка типов выполняются во время компиляции. Код выглядит так:

class LoginController {

Container *container;
TextBox *usernameBox;
TextBox *passwordBox;

public:

LoginController() :
container(new Container()),
usernameBox(new TextBox()),
passwordBox(new TextBox())
{
container->addChild(username);
container->addChild(password);

Button *button = new Button("Log In");
container->addChild(button);
button->setAction([](){
this->login();
})
}

void login() {
string username = usernameBox->getText();
string password = passwordBox->getText();
sendLoginRequest(username, password);
}

};
2

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

Я думаю, вам не нужен контейнер, содержащий все виджеты. Классы могут содержать указатель на конкретный экземпляр виджета, который им нужно использовать. Вы можете передать указатель в качестве параметра в конструктор; другой вариант — использовать внедрение зависимостей (например, кенгуру может быть использован для вашей проблемы).

3

Я везде читал, что RTTI это плохо и его следует избегать

Я сомневаюсь что ты читаешь тот везде, как это чистая фигня.

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

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

А те, кто интересуется каким-то редким интерфейсом или конкретным классом, могут навсегда вызвать dynamic_cast. Коллекции лучше просто держать в простоте и ограничивать коллекционирование. Возможно добавление нескольких специальных форм для действительно распространенных семейств виджетов.

1

Проблема здесь в том, что ваш дизайн задом наперед. Вы хотите визуализировать виджет, но виджет не имеет конкретных функций визуализации. Вы должны попросить Виджет отрендерить себя, где он может отображать текст / изображение / все, что он хочет.

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