Closed. This question is
opinion-based。它当前不接受答案。
想改善这个问题吗?更新问题,以便
editing this post用事实和引用来回答。
2年前关闭。
Improve this question
我已经用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
如您所见,我在这里有两种类型的菜单;
目录(MS-DOS文件夹)菜单,其中包含其他菜单。激活后,它们将打开/关闭。例如:Root directory
现在已打开,您可以看到其中的所有菜单。如果关闭,则[-]
变成[+]
,您将看不到任何其他菜单。 操作(MS-DOS文件)菜单,这些菜单链接到功能。激活后,他们调用链接到的函数。例如:Open snakegame
可以链接到函数startSnakeGame()
,该函数将关闭菜单并启动蛇游戏。 我已经编码了两个可行的实现以获得所需的结果,而我只是想知道,我应该使用哪个?第一种方法是,我只有一个名为
Menu
的类,并且所有成员变量和方法都编码为一个类。另一种方法是,我将这两种不同类型的菜单分为两个类,并具有一个公共(public)基类。
这是一些成员变量,我现在将它们分为三个部分(基类,目录类和操作类),但可以将它们组合为一个类。
基本菜单:
parent
=菜单(目录一),将this/self
作为子菜单保存在列表/vector 中(请参见下文)。 label
=显然,在打印菜单时显示的标签。 selected
= bool 值,用于告知当前是否已选择菜单(由鼠标指向)。 目录菜单:
subMenus
=在其中包含其他菜单的列表或 vector (在C++中)。 open
=一个 bool 值,它告诉我们菜单是打开还是关闭。 操作菜单:
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()
}
第二种方法更可取(实际上是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;
}