c – 重构建议:如何避免在此OO设计中进行类型检查
作者:互联网
我正在寻找有关重构的建议,以改善我的课程设计并避免类型检查.
我正在使用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()时看到显式类型检查,然后转换为派生类型.有哪些选项可以重构这个以避免类型检查和强制转换?
解决方法:
IMO,重构的起点是这些陈述:
1. m_pos.push_back( MenuPlusIter( m_menu, m_menu->m_items.begin() ) );
2. m_pos.push_back( MenuPlusIter( submenu, submenu->m_items.begin() ) );
相同类型的声明重演的事实是,IMO,需要重构的标志.
如果你可以在基类的方法中考虑因子(1),然后在派生类中重写它以考虑特定的行为(2),那么你可以把它放在执行中.
如果我错了,请纠正我:想法是菜单中有项目,并且每个项目都有一个与之关联的动作,当检测到某个事件时会触发该动作.
现在,当您选择的项目是子菜单时,执行操作具有以下含义:激活子菜单(我在一般意义上使用激活).当项目不是子菜单时,Execute是一个不同的野兽.
我没有完全理解你的菜单系统,但在我看来你有一种层次结构菜单/子菜单(位置),以及根据节点类型触发的一些动作.
我设想的是菜单/子菜单关系是一个层次结构,允许您定义叶节点(当您没有子菜单时)和非叶节点(子菜单).一个叶子节点调用一个动作,一个非叶子节点调用一个不同类型的动作来处理激活一个子菜单(这个动作回到菜单系统,所以你没有封装有关菜单系统的知识,你只需要将动作转发到菜单系统).
不知道这对你有意义.
标签:dynamic-cast,c,object,refactoring,type-conversion 来源: https://codeday.me/bug/20190726/1542162.html