クラスの設計を改善し、型チェックを回避するためのリファクタリングに関するアドバイスを探しています。
Command デザイン パターンを使用してメニュー ツリーを作成しています。メニューの項目にはさまざまな種類があります (たとえば、即時アクション [「保存」など]、状態に応じてチェック/アイコンを表示するトグル オン/オフ プロパティ [「斜体」など] など)。重要なことに、画面上の現在のメニューを (横に表示するのではなく)置き換えるサブメニューもあります。もちろん、これらのサブメニューには独自のメニュー項目のリストが含まれており、さらにネストされたサブメニューを持つことができます。
コードは次のようなものです(プレゼンテーションを簡単にするためにすべて公開されています):
// Abstract base class
struct MenuItem
{
virtual ~MenuItem() {}
virtual void Execute() = 0;
virtual bool IsMenu() const = 0;
};
// Concrete classes
struct Action : MenuItem
{
void Execute() { /*...*/ }
bool IsMenu() const { return false; }
// ...
};
// ... other menu items
struct Menu : MenuItem
{
void Execute() { /* Display menu */ }
bool IsMenu() const { return true; }
// ...
std::vector<MenuItem*> m_items;
typedef std::vector<MenuItem*>::iterator ItemIter;
};
メイン メニューは Menu の単なるインスタンスであり、サブメニューに出入りする方法を含め、別のクラスがメニューの位置を追跡します。
struct Position
{
Position( Menu* menu )
: m_menu( menu )
{
// Save initial position
m_pos.push_back( MenuPlusIter( m_menu, m_menu->m_items.begin() ) );
}
// Ignore error conditions for simplicity
void OnUpPressed() { m_pos.back().iter--; }
void OnDownPressed() { m_pos.back().iter++; }
void OnBackPressed() { m_pos.pop_back(); }
void OnEnterPressed()
{
MenuItem* item = *m_pos.back().iter;
// Need to behave differently here if the currently
// selected item is a submenu
if( item->IsMenu() )
{
// dynamic_cast not needed since we know the type
Menu* submenu = static_cast<Menu*>( item );
// Push new menu and position onto the stack
m_pos.push_back( MenuPlusIter( submenu, submenu->m_items.begin() ) );
// Redraw
submenu->Execute();
}
else
{
item->Execute();
}
}
private:
struct MenuPlusIter
{
Menu* menu;
Menu::ItemIter iter;
MenuPlusIter( Menu* menu_, Menu::ItemIter iter_ )
: menu( menu_ )
, iter( iter_ )
{}
};
Menu* m_menu;
std::vector<MenuPlusIter> m_pos;
};
重要な関数は Position::OnEnterPressed() です。ここでは、MenuItem::IsMenu() への呼び出しで明示的な型チェックが行われ、派生型にキャストされます。タイプチェックとキャストを回避するためにこれをリファクタリングするためのいくつかのオプションは何ですか?