例如我可以有这样的东西。
A
B
ba
bb
C
Ca
D
现在我有一个2D数组,但这不是很通用,因为如果我想把最大的子级从2扩展到3,我需要另一个维度。 有什么建议吗?
复合模式在这里是一个合适的应用。
(来自维基百科:) http:/en.wikipedia.orgwikiComposite_pattern
在你的情况下。
你可以根据菜单元素在复合 "SubMenu "中的插入顺序来实现所需的排序,方法是为每个SubMenu对象实现一个计数器:每次调用 aSubMenu.add(newMenuItemOrSubMenu)
, aSubMenu
应该递增它自己的计数器,并给新的项目加上订购号的标签。(具体的实现细节由您决定,您完全不必使用单独的计数器,只需使用一个列表或数组)
也许这。
class MenuNode
{
public:
MenuNode(std::string new_label);
void Add(MenuNode * new_node);
private:
std::string label;
std::vector<MenuNode *> children; // changed to vector to preserve order
};
用法:
MenuNode menu("root"),
file("File"),
edit("Edit"),
open("Open..."),
close("Close"),
save("Save..."),
prefs("Preferences"),
yes_foo("Activate Foo"),
no_foo("Deactivate Foo");
menu.Add(&file);
menu.Add(&edit);
file.Add(&open);
file.Add(&close);
file.Add(&save);
edit.Add(&prefs);
prefs.Add(&yes_foo);
prefs.Add(&no_foo);
代表:
Main Menu
File
Open...
Close
Save...
Edit
Preferences
Activate Foo
Deactivate Foo
小心这个例子的明显缺陷,依赖临时变量的地址(可能)。 你不可能在一个函数中创建这个并返回它。
实现的琐碎部分也是缺失的,例如在示例代码中没有办法遍历节点的私有状态。
sampson-chen提到的复合设计模式是我为一个小型开发者监控器做的一个实现,让你从菜单结构中选择一些测试方法的正确方法。
我有一个基类 "Menu Entry",从中派生出子菜单和叶子(菜单项)。叶子只是执行一些东西,而子菜单则是打开另一个菜单级别。
例如,基类可以类似于这样(当你喜欢使用shared_pointers时)。
/*************************************************************//*!
* @brief The base class for all menu entry types (sub menus and sub menu entries/items)
******************************************************************/
class MenuEntry : public boost::enable_shared_from_this<MenuEntry>
{
public:
virtual ~MenuEntry(){}
/**************************************************************//*!
* @brief Default implementation to add menu entries; has to be re implemented
* @param[in] newSubMenuEntry - the new menu entry (another sub menu or leaf)
**************************************************************/
virtual void add(boost::shared_ptr<MenuEntry> newSubMenuEntry)=0;
/*****************************************************************//*!
* @brief Default implementation for call to menu entries; always returns false
* @return false - the function has not been re implemented
****************************************************************/
virtual bool call(void)
{
// the member function has not been re-implemented
return false;
}
/*****************************************************************************//*!
* @brief Default implementation, has to be reimplemented
* @return emptyVector - an empty vector
*********************************************************************************/
virtual std::vector<boost::shared_ptr<MenuEntry> > getChildren(void)
{
std::vector<boost::shared_ptr<MenuEntry> > emptyVector;
return emptyVector;
}
/*******************************************************************************//*!
* @brief Gives a pointer to the parent of the actual menu entry
* @return m_parent - pointer to the parent
******************************************************************************/
boost::shared_ptr<MenuEntry> parent(void)
{
return m_parent;
}
/***************************************************************************//*!
* @brief Default implementation for selecting a menu entry
* @param[in] desiredMenuEntry - the desired menu entry which shall be selected
* @return notExisting - a pointer to <b>this</b>
**********************************************************************************/
virtual boost::shared_ptr<MenuEntry> select(boost::shared_ptr<MenuEntry> desiredMenuEntry)=0;
/**************************************************************************//*!
* <B>Criticality: C0 \n\n</B>
* @brief Sets a pointer to the parent of new menu entry
* @param[in] pointerToParent - pointer to the parent
****************************************************************************/
void setParent(boost::shared_ptr<MenuEntry> pointerToParent)
{
m_parent = pointerToParent;
}
/***************************************************************************//*!
* @brief Default implementation for destroying children
*****************************************************************************/
virtual void destroy(void)=0;
protected:
/************************************************************************//*!
* @brief Constructor for a menu entry (sub menu or leaf)
* @param[in] menuEntryName - the name for the sub menu or leaf
*************************************************************************/
MenuEntry(std::string menuEntryName)
{
m_menuEntryName = menuEntryName;
}
};
在select方法中,我通过返回值来检查是否有一个叶子在执行某事,或者一个子菜单,我必须为它改变指针。
为了方便起见,你可以添加一些方法来查找子菜单中所有的子菜单条目,以便显示,或者添加一些方法来构建一个带有实际菜单路径的标题行或类似的方法...... 另一个想法是扫描你的菜单树的方法,以避免双重菜单条目asf。
使用一个树。反正最好是用树来定义。
其中:rootNode连接到 A
, B
, C
, D
. B
连接到 ba
和 bb
. C
连接到 Ca
.等等。
我在我的 Github
ConsoleMenu.h
typedef int (*MENU_FUNCTION_POINTER)();
typedef struct _MENU MENU;
typedef MENU* MENU_POINTER;
struct _MENU
{
bool isValid; // C++ can not compare struct is NULL
string text;
MENU_FUNCTION_POINTER func;
bool isOpen;
MENU_POINTER childMenu;
MENU_POINTER parent;
};
class ConsoleMenu
{
private:
static MENU _chapters[];
static MENU _chapter1[];
static MENU _chapter2[];
};
ConsoleMenu.cpp
MENU ConsoleMenu::_chapters[] = {
{true, "Chapter 1", NULL, false, ConsoleMenu::_chapter1, NULL},
{true, "Chapter 2", NULL, false, ConsoleMenu::_chapter2, NULL},
{true, "Chapter 3", NULL, false, NULL, NULL},
{true, "Chapter 4", NULL, false, NULL, NULL},
{false, "unused", NULL, false, NULL, NULL}
};
MENU ConsoleMenu::_chapter1[] = {
{true, "Example 1.1", Example1, false, NULL, ConsoleMenu::_chapters},
{true, "Example 1.2", Example2, false, NULL, ConsoleMenu::_chapters},
{false, "unused", NULL, false, NULL, NULL}
};
MENU ConsoleMenu::_chapter2[] = {
{true, "Example 2.1", NULL, false, NULL, ConsoleMenu::_chapters},
{true, "Example 2.2", NULL, false, NULL, ConsoleMenu::_chapters},
{true, "Example 2.3", NULL, false, NULL, ConsoleMenu::_chapters},
{false, "unused", NULL, false, NULL, NULL}
};