例如我可以有这样的东西,
A
B
ba
bb
C
Ca
D
现在我有一个二维数组,但这不是很一般,因为如果我想将最大子级别从 2 扩展到 3,我需要另一个维度。有什么建议吗?
复合模式在这里是一个合适的应用:
(来自维基百科:)http://en.wikipedia.org/wiki/Composite_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 提到的复合设计模式是我为小型开发人员监视器所做的实现的正确方法,让您从菜单结构中选择一些测试方法。
我有一个基类“菜单条目”,从中派生子菜单和叶子(菜单条目)。叶子只是执行某些操作,而子菜单会打开另一个菜单级别。
例如,基类可能与此类似(当您喜欢使用 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
。等等
控制台菜单.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[];
};
控制台菜单.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}
};