Best way to create a console menu class hierarchy?

I created a console menu in both C ++ and Python, but I believe that the language here is not too big, since I am asking about the structure of classes.

So what I'm trying to achieve is a menu similar to MS-DOS, where I can have parent menus (folders) and action menus (files). Here's how it might look on the console after printing:

[-] 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 

So, as you can see, I have two types of menus:

  • The directory (MS-DOS folder) where other menus are stored. When activated, they open / close. For example: The Root directory now open, and you can see all the menus inside it. If it closes, [-] turns into [+] , and you cannot see any of the other menus.
  • The action menu (MS-DOS) that are associated with the function. When they are activated, they invoke the function with which they are associated. For example: Open snakegame can be associated with the startSnakeGame() function, which closes the menu and starts the snake game.

I have already coded two working implementations to get the desired result, and I'm just wondering which one should I use? The first way: I have only one class called Menu , and it has all the member variables and methods encoded into one class. Another way is where I got these two different types of menus divided into two classes, with a common base class.


Here are some member variables, now I divide them into three sections (base class, catalog class, and action class), but they can be combined into one class.

Basic menu:

  • parent = A menu (one directory) that contains this/self inside the list / vector as a child (see below).
  • label = Obviously this label is displayed when a menu is printed.
  • selected = Boolean value that indicates that the menu is currently selected (fe. point by mouse).

Directory menu:

  • subMenus = A list or vector (in C ++) that contains other menus inside it.
  • open = Boolean value indicating that the menu is open or closed.

Action Menu:

  • action = Pointer to a function that is called when this menu is activated.

As you can see, there are only a few variables that differ from another class, and it can be set so that if action == 0 (without action), the menu automatically changes open to false/true , depending on it, the current value. Thus, the action menu will be terminated, and only the bottom side will be that the action menu will contain subMenus and closed without use.


It may be just one opinion, but I thought about it for a while and could not find one way better than the other, both have their own advantages and disadvantages, and both of them work well. Therefore, I ask about your opinion, and I would be pleased to hear if anyone has any reason why they choose each other. Basically I ask for reasons, I don't care about your opinion.

There will be no other types of menus except a folder and a file, so the base class cannot be used for anything else.


Edit: A simple Python and C ++ example on how menus are used:

Python with one class:

 # 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:

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

C ++ with one class:

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

C ++ with several classes:

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

2nd Edit: I only implemented both methods in Python and only a single-class method in C ++. So I started coding a multiclass image in C ++, just for fun and practice, and I had a problem; having one base class, I cannot add this to the parent subMenus -vector, since the base class does not have subMenus , and the base class cannot know DirectoryMenu .

So I have to make my way, which is a BIG minus. Can't someone come up with a good way to implement it?

 BaseMenu::BaseMenu(BaseMenu* parent) : m_parent(parent) // works { m_parent->addSubMenuk(this); // BaseMenu doesn't have Directory addSubMenu() } 
+6
source share
2 answers

The two methods are really close to each other in performance, etc., so it was hard to understand. However, there was one reason to choose one of them: Logic and rules in OOP. I had to divide it into 3 classes: BaseMenu , ActionMenu and DirectoryMenu .

The solution of β€œtwo classes cannot know each other”, the problem here can be implemented, for example, as iogane gamba puti fon gu . However, the definition of the abstract methods addSubMenu() and removeSubMenu() in BaseMenu will be the same as against the rules, since it has only one class, so it is not an option in a different way.

As a result, I used callbacks and overloaded the pointer operator ( * ). Now it returns a pointer to an instance of another class and calls its methods depending on the type.

0
source

The second method is much preferable (in fact, it is http://sourcemaking.com/design_patterns/composite ). The implementation is separate from the interface - it is easy to add new menu items. Moreover, this structure will work well with the visitor of the template http://sourcemaking.com/design_patterns/visitor

you can add code to this trace and see the result:

 #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; } 
+2
source

All Articles