[英]Avoiding dynamic_cast in GUI design
我正在設計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
}
}
如何避免動態投射? 我負擔不起每種窗口小部件類型的容器,因為我的GUI必須與用戶定義的窗口小部件一起使用。 另外,我必須為每個小部件都提供一個容器,這樣用戶就不必自己存儲小部件。
為什么需要鑄造?
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
}
我認為您不需要容納所有小部件的容器。 這些類可以保存一個指向它們需要使用的小部件的具體實例的指針。 您可以將指針作為構造函數上的參數傳遞。 另一個選擇是使用依賴注入(例如,可以將wallaroo用於您的問題)。
擴大我的評論...
根據您的代碼,我想您打算讓我為添加到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);
}
};
我到處都讀到RTTI不好,應該避免
我懷疑您到處都讀到這,因為那簡直是胡說八道。
RTTI是一個非常好的功能。 盡管絕對可以避免,甚至可以使用更好的工具 。 一個好的層次結構被設計為僅通過虛擬函數就可以訪問基類接口。 在那種情況下,您不需要任何強制轉換,只需調用虛函數即可,它將做正確的事情。
甚至您的GetText也可能是一個合理的選擇,默認實現返回一個空字符串。 也許在功能查詢工具的陪伴下,它報告實際文本的存在。 因此,大多數客戶都可以通過空字符串來結束通話,而其他人則可以進行檢查。
那些只對具體類感興趣的罕見接口的人可以永久調用dynamic_cast。 收集最好只是保持簡單並限制收集。 也許為真正常見的Widget系列添加一些特殊形式。
這里的問題是您的設計是落后的。 您想渲染一個小部件,但是該小部件沒有具體的可渲染功能。 您必須要求Widget自己渲染,它可以在任何地方渲染文本/圖像/任何內容。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.