其他分享
首页 > 其他分享> > c – 重构建议:如何避免在此OO设计中进行类型检查

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