6

我已经在 C++ 和 Python 中创建了一个控制台菜单,但我认为语言在这里的作用并不大,因为我在询问类的结构。

所以我想要实现的是一个类似于 MS-DOS 的菜单,其中我可以有父菜单(文件夹)和操作菜单(文件)。这是它在控制台中的样子,一旦打印出来:

[-] Root directory
        Open snakegame
    [-] First sub directory
            Print out stupid messages
        [+] Closed directory in open directory
            Shutdown computer
    [+] These directories are closed
    [+] You can't see the content inside them
        Quit menu

如您所见,我在这里有两种类型的菜单;

  1. 目录(MS-DOS 文件夹)菜单,其中包含其他菜单。激活后,它们会打开/关闭。例如:Root directory现在已打开,您可以看到其中的所有菜单。如果它被关闭,[-]则会变成[+]并且您看不到任何其他菜单。
  2. 链接到功能的操作(MS-DOS 文件)菜单。激活后,它们会调用它们链接到的函数。例如:Open snakegame可以链接到一个函数startSnakeGame(),该函数将关闭菜单并启动贪吃蛇游戏。

我已经编写了两个工作实现来获得想要的结果,我只是想知道,我应该使用哪一个?第一种方法是,我只有一个名为 的类Menu,它的所有成员变量和方法都编码到一个类中。另一种方法是,我将这两种不同类型的菜单分成两个类,具有一个共同的基类。


这里有一些成员变量,我现在将它们分成三部分(基类、目录类和动作类),但它们可以合并为一个类。

基本菜单:

  • parent= 作为子项保存在列表/向量中的菜单(目录一)this/self(见下文)。
  • label= 显然是打印菜单时显示的标签。
  • selected=布尔值,它告诉当前是否选择了菜单(例如鼠标指向)。

目录菜单:

  • subMenus= 包含其他菜单的列表或向量(在 C++ 中)。
  • open= 一个布尔值,它告诉菜单是打开还是关闭。

动作菜单:

  • action= 指向激活此菜单时调用的函数的指针。

正如您所看到的,只有少数变量与其他类不同,并且可以设置为如果action == 0(无操作)则菜单会根据其当前值自动open更改false/true。这样操作菜单将被终止,唯一的缺点是操作菜单将保留subMenus且不closed使用。


这可能是一个人的意见,但我一直在思考这个问题,并没有找到一种优于其他方式的方法,它们都有其优点和缺点,并且都运作良好。所以我在问你的意见,我很想听听是否有人有任何理由选择一个而不是另一个。基本上我是在问原因,我不在乎你的意见。

除了文件夹和文件之外,没有其他菜单类型,因此基类不能用于其他任何事情。


编辑:关于如何使用菜单的简单 Python 和 C++ 示例:

Python 只有一个类:

# Using default param. here to set "action = None" or "action = toggleOpen()"
root = Menu(None, "Root directory")
snake = Menu(root, "Open snakegame", startSnakeGame)
sub1 = Menu(root, "First sub directory")
printMsg = Menu(sub1, "Print out stupid messages")
...

具有多个类的 Python:

# With multiple classes, action parameter no longer exists
root = DirectoryMenu(None, "Root directory")
snake = ActionMenu(root, "Open snakegame", startSnakeGame)
...

一个类的 C++:

Menu* root = new Menu(0, "Root directory");
Menu* snake = new Menu(&root, "Open snakegame", &startSnakeGame);
...

具有多个类的 C++:

DirectoryMenu* root = new DirectoryMenu(0, "Root directory");
ActionMenu* snake = new ActionMenu(&root, "Open snakegame", &startSnakeGame);
...

第二次编辑:我只在 Python 中实现了两种方式,在 C++ 中只实现了一种方式。所以我也开始用 C++ 编写多类方式,只是为了好玩和练习,我遇到了一个问题;有一个基类,我不能添加this到父类的subMenus-vector,因为基类不拥有subMenus,并且基类不知道DirectoryMenu

所以我将不得不通过这个破解我的方式,这是一个很大的减号。除非有人能想出一个好的方法来实现它?

BaseMenu::BaseMenu(BaseMenu* parent)
: m_parent(parent) // works
{
    m_parent->addSubMenuk(this); // BaseMenu doesn't have Directory's addSubMenu()
}    
4

2 回答 2

2

第二种方式更可取(实际上它是http://sourcemaking.com/design_patterns/composite)。实现与界面分离 - 易于添加新菜单项。此外,这种结构可以很好地与模式访问者http://sourcemaking.com/design_patterns/visitor配合使用

您可以添加到此代码跟踪并查看输出:

#include <vector>
#include <boost/shared_ptr.hpp>

class ActionMenu;
class SubMenu;
class Menu;

typedef boost::shared_ptr<Menu> MenuPtr;
typedef boost::shared_ptr<SubMenu> SubMenuPtr;
typedef boost::shared_ptr<ActionMenu> ActionMenuPtr;

//visitor
class Action
{
public:
    virtual void visit(ActionMenu* actionMenu) = 0;
    virtual void visit(SubMenu* subMenu) = 0;
};

//element
class Menu
{
public:
    virtual void Accept(Action& action) = 0;
};

//menus
class SubMenu : public Menu
{
public:
    virtual ~SubMenu() 
    {
    }

    unsigned long GetMenuCount()
    {
        return m_menus.size();
    }

    MenuPtr GetMenyByIndex(unsigned long index)
    {
        return m_menus[index];
    }

    void AddMenu(const MenuPtr& menu)
    {
        m_menus.push_back(menu);
    }

    virtual void Accept(Action& action)
    {
        action.visit(this);
    }

    void ShowMenu()
    {
    }

    void ChangeStyle()
    {
    }
private:
    std::vector<MenuPtr> m_menus;
};

class ActionMenu : public Menu
{
public:
    virtual ~ActionMenu() 
    {
    }

    virtual void Accept(Action& action)
    {
        action.visit(this);
    }

    void DoCommand()
    {
    }

    void ChangeImage()
    {
    }
};

//visitors

class StyleAction : public Action
{
public:
    virtual ~StyleAction() 
    {
    }

    virtual void visit(ActionMenu* actionMenu)
    {
        actionMenu->ChangeImage();
    }

    virtual void visit(SubMenu* subMenu)
    {
        subMenu->ChangeStyle();
    }
};

class MenuAction : public Action
{
public:
    virtual ~MenuAction() 
    {
    }

    virtual void visit(ActionMenu*actionMenu)
    {
        actionMenu->DoCommand();
    }

    virtual void visit(SubMenu* subMenu)
    {
        subMenu->ShowMenu();
    }
};

int main(int argc, char* argv[])
{
    SubMenuPtr rootMenu(new SubMenu());
    SubMenuPtr fileMenu(new SubMenu());
    SubMenuPtr userMenu(new SubMenu());
    MenuPtr open(new ActionMenu());
    MenuPtr save(new ActionMenu());
    MenuPtr close(new ActionMenu());
    fileMenu->AddMenu(open);
    fileMenu->AddMenu(save);
    fileMenu->AddMenu(close);
    rootMenu->AddMenu(fileMenu);
    rootMenu->AddMenu(userMenu);

    StyleAction sa;
    MenuAction ma;

    //iterate over root menu
    //recursively can bypass all the menu items, the structure of the tree
    for (unsigned int i = 0; i < rootMenu->GetMenuCount(); ++i)
    {
        rootMenu->GetMenyByIndex(i)->Accept(sa);
        rootMenu->GetMenyByIndex(i)->Accept(ma);
    }

    return 0;
}
于 2012-11-29T22:24:09.277 回答
0

这两种方式在性能等方面非常接近,所以很难弄清楚。但是,选择其中之一是有原因的:OOP 中的逻辑和规则。我不得不将它分成 3 个类BaseMenuActionMenuDirectoryMenu.

在这里解决“两个班级不能互相认识”的问题可以像iogane gamba puti fon gu建议的那样完成。但是,定义抽象方法addSubMenu()removeSubMenu()inBaseMenu与只有一个类一样违反规则,因此这不是另一种方式的选择。

我最终得到的是使用回调和重载指针(*)运算符。它现在返回一个指向另一个类的实例的指针,并根据类型调用它的方法。

于 2012-12-04T20:56:22.553 回答