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

ご覧のとおり、ここには2種類のメニューがあります。

  1. ディレクトリ(MS-DOSフォルダ)メニュー。他のメニューをその中に保持します。起動すると、開閉します。例:Root directoryが開いており、その中のすべてのメニューを見ることができます。閉じられるとに[-]変わり[+]、他のメニューは表示されなくなります。
  2. 関数にリンクされているアクション(MS-DOSファイル)メニュー。有効にすると、リンク先の関数を呼び出します。例:メニューを閉じてヘビゲームを開始するOpen snakegame関数にリンクすることができます。startSnakeGame()

必要な結果を得るために、すでに2つの実用的な実装をコーディングしましたが、どちらを使用すればよいのでしょうか。最初の方法は、と呼ばれるクラスが1つだけで、Menuすべてのメンバー変数とメソッドが1つのクラスにコード化されている場合です。もう1つの方法は、これら2つの異なるタイプのメニューを2つのクラスに分け、共通の基本クラスを使用することです。


ここにいくつかのメンバー変数があります。ここではそれらを3つのセクション(基本クラス、ディレクトリクラス、アクションクラス)に分割しますが、1つのクラスに組み合わせることができます。

基本メニュー:

  • parent=子としてリスト/ベクトル内に保持されるメニュー(ディレクトリ1)this/self(以下を参照)。
  • label=明らかに、メニューの印刷時に表示されるラベル。
  • selected=メニューが現在選択されているかどうかを示すブール値(マウスでポイントされている場合など)。

ディレクトリメニュー:

  • subMenus=リストまたはその中に他のメニューを保持するベクトル(C ++の場合)。
  • open=メニューが開いているか閉じているかを示すブール値。

アクションメニュー:

  • action=このメニューがアクティブになったときに呼び出される関数へのポインタ。

ご覧のとおり、他のクラスと異なる変数はごくわずかであり、action == 0(アクションなしで)メニューが現在の値openに応じて自動的に変わるように設定できます。false/trueこのようにして、アクションメニューは終了しますが、唯一の欠点は、アクションメニューが保持されて使用subMenusされclosedないことです。


これはすべて自分の意見かもしれませんが、私はこれについてしばらく考えていましたが、他の方法よりも優れた方法を見つけることができませんでした。どちらにも長所と短所があり、どちらもうまく機能します。だから私はあなたの意見について尋ねています、そして誰かが彼らがどちらかを選ぶ理由があるかどうか聞いてみたいです。基本的に私は理由を尋ねています、私はあなたの意見を気にしません。

フォルダとファイル以外のメニュータイプはないため、基本クラスを他の目的に使用することはできません。


編集:メニューの使用方法に関する簡単なPythonとC ++の例:

クラスが1つしかない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)
...

1つのクラスを持つ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);
...

2番目の編集: Pythonでは両方の方法のみを実装し、C++では1クラスの方法のみを実装しました。それで、楽しみと練習のためだけに、C ++でもマルチクラスの方法をコーディングし始めましたが、問題が発生しました。基本クラスが1つある場合、基本クラスはを所有しておらず、基本クラスはを知ることができないため、this親のsubMenus-vectorに追加することはできません。subMenusDirectoryMenu

ですから、これをハックする必要があります。これは大きなマイナスです。誰かがそれを実装するための良い方法を考えられない限り?

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

2 に答える 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

パフォーマンスなどで2つの方法は非常に近いので、理解するのは少し難しかったです。ただし、どちらかを選択する理由が1つありました。それは、OOPのロジックとルールです。私はそれを3つのクラスに分割しなければなりませんでした: BaseMenu、、。ActionMenuDirectoryMenu

ここで「2つのクラスがお互いを知ることができない」問題を解決することは、iogane gamba puti fon gu提案されたように行うことができたでしょう。ただし、抽象メソッドaddSubMenu()removeSubMenu()inBaseMenuを定義することは、クラスが1つしかない場合と同じようにルールに反するため、他の方法に勝るオプションではありません。

私が最終的に得たのは、コールバックを使用し、pointer(*)演算子をオーバーロードすることです。他のクラスのインスタンスへのポインタを返し、タイプに応じてそのメソッドを呼び出すようになりました。

于 2012-12-04T20:56:22.553 に答える