使用shared_ptr处理可靠的命令模式

发布于 2024-11-03 02:02:10 字数 1385 浏览 5 评论 0原文

我正在尝试在库中实现一个非常干净的命令模式。

我现在有以下结构(一些部分仍在完成中):

  1. 用户(客户端代码)有一些对象,称其为“Manager”
  2. Manager 拥有一个 shared_ptrshared_ptr的集合。 Foo>
  3. Manager 通过返回 shared_ptr 提供对集合的访问
  4. 我有一个 Command 抽象类和命令层次结构用于执行的操作Foo
  5. 客户端代码不应该调用Command::execute(),只有Manager应该 >,Manager::execute(shared_ptr),以便它可以处理撤消/重做

我想遵循以下规则:

  1. 用户(客户端代码)有一些对象,称之为“经理”
  2. 经理持有shared_ptr 的集合
  3. Manager 通过返回 shared_ptr 提供对集合的访问
  4. 我有一个 Command 抽象类和对 Foo 执行操作的命令层次结构
  5. 客户端代码不能(没有解决方法)调用 Command::execute() , 仅有的Manager 可以Manager::execute(shared_ptr),这样它就可以处理undo/redo并获取非常量智能指针
  6. 即使用户初始化了 Command,Manager 也必须能够允许 Command 对象访问和修改 shared_ptr > objecst 与 shared_ptr

我只是想找出处理发出 shared_ptr 同时允许数字 5 和 6 工作的最佳方法。

有没有任何示例/设计模式可以让我学习?与我已经拥有/正在做的事情相比,这是一个好主意吗?

I am trying to implement a very clean Command Pattern in a library.

I have the following structure right now (a few parts are still being finished up):

  1. users (client-code) have some Object, call it "Manager"
  2. Manager holds a collection of shared_ptr<Foo>
  3. Manager provides access to the collection by returning shared_ptr<Foo>
  4. I have a Command abstract class and a hierarchy of commands for actions to perform on Foo
  5. Client code should not call Command::execute(), only Manager should, Manager::execute(shared_ptr<Command>), so that it can handle undo/redo

I would like to follow the following rules:

  1. users (client-code) have some Object, call it "Manager"
  2. Manager holds a collection of shared_ptr<Foo>
  3. Manager provides access to the collection by returning shared_ptr<const Foo>
  4. I have a Command abstract class and a hierarchy of commands for actions to perform on Foo
  5. Client code cannot (without workarounds) call Command::execute(), only Manager can, Manager::execute(shared_ptr<Command>), so that it can handle undo/redo and get non-const smart pointers
  6. A Manager must be able to allow Command objects to access and modify shared_ptr<Foo> even though the user initializes Command objecst with shared_ptr<const Foo>

I am just trying to figure out the best way to handle giving out shared_ptr<const Foo> while allowing number 5 and 6 to work.

Is there any example/design pattern that does this which I could learn from? Is this a good idea compared to what I already have/am working on?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

梦幻的心爱 2024-11-10 02:02:10

我认为这个 密钥模式 应该适合您:

class CommandExecuteKey{
private:
  friend class Manager;
  CommandExecuteKey(){}
};

class Command{
public:
  // usual stuff
  void execute(CommandExecuteKey);
};

class Manager{
public
  void execute(Command& cmd){
    // do whatever you need to do
    cmd.execute(CommandExecuteKey());
  }
};

现在,Command 不知道有关 Manager 的任何信息,只知道所需的密钥对于执行功能。用户将无法直接调用 execute 方法,因为只有 Manager 可以创建 CommandExecuteKey 对象,这要归功于私有构造函数和 <代码>友谊。

int main(){
  Command cmd;
  Manager mgr;
  //cmd.execute(CommandExecuteKey()); // error: constructor not accessible
  mgr.execute(cmd); // works fine, Manager can create a key
}

现在,谈谈你的第六点:
当您输入命令时,在所有 shared_ptr 中搜索正确的对象(使用保存的命令 shared_ptr 作为搜索键),然后传递从内部 shared_ptr 返回到命令的可变指针。

I think this passkey pattern should be the right thing for you:

class CommandExecuteKey{
private:
  friend class Manager;
  CommandExecuteKey(){}
};

class Command{
public:
  // usual stuff
  void execute(CommandExecuteKey);
};

class Manager{
public
  void execute(Command& cmd){
    // do whatever you need to do
    cmd.execute(CommandExecuteKey());
  }
};

Now, Command doesn't know anything about Manager, only about the key that is needed for the execute function. The user won't be able to call the execute method directly, as only Manager can create CommandExecuteKey objects, thanks to a private constructor and friendship.

int main(){
  Command cmd;
  Manager mgr;
  //cmd.execute(CommandExecuteKey()); // error: constructor not accessible
  mgr.execute(cmd); // works fine, Manager can create a key
}

Now, for your 6th point:
When you get the command in, search all your shared_ptr<Foo>s for the correct object (using the saved shared_ptr of the command as a search-key) and then pass that mutable one from your internal shared_ptrs back to the command.

浅笑轻吟梦一曲 2024-11-10 02:02:10

因为否则对我来说没有任何意义,我将假设

  • 您的库提供了 Manager 类(或至少是基类),并且
  • 客户端必须使用该类来调用一个命令

在这种情况下,也许这样的方法可以工作:

void Manager::execute(Command& cmd, shared_ptr<Foo const> const& target)
{
    shared_ptr<Foo> mutableTarget = this->LookupMutableFoo(mutableTarget); // throws if not found
    cmd.execute(mutableTarget); // client cannot invoke without mutable pointer
}

// or, if the "target" needs to be stored in the Command you could use something like this:
void Manager::execute(Command& cmd)
{
    shared_ptr<Foo> mutableTarget = this->LookupMutableFoo(cmd.GetTarget()); // throws if not found
    cmd.execute(mutableTarget); // client cannot invoke without mutable pointer
}

我不确定使用 const 是否是这里的最佳解决方案。也许您应该将 Foo 对象包装在例如 ClientFoo 对象中。管理器仅分发指向 ClientFoo 的指针。然后,管理器可以(例如通过friend)从ClientFoo获取Foo,并使用它来调用Command >。

Since it wouldn't make any sense to me otherwise, I'm going to assume that

  • your library provides the Manager class (or at least a base class), and
  • clients have to use that class to invoke a Command.

In that case, maybe something like this could work:

void Manager::execute(Command& cmd, shared_ptr<Foo const> const& target)
{
    shared_ptr<Foo> mutableTarget = this->LookupMutableFoo(mutableTarget); // throws if not found
    cmd.execute(mutableTarget); // client cannot invoke without mutable pointer
}

// or, if the "target" needs to be stored in the Command you could use something like this:
void Manager::execute(Command& cmd)
{
    shared_ptr<Foo> mutableTarget = this->LookupMutableFoo(cmd.GetTarget()); // throws if not found
    cmd.execute(mutableTarget); // client cannot invoke without mutable pointer
}

I'm not sure though if using const is the best solution here. Maybe you should wrap your Foo objects in e.g. ClientFoo objects. The manager only hands out pointers to ClientFoo. The manager can then (e.g. via friend) get the Foo from the ClientFoo, and use it to invoke the Command.

请爱~陌生人 2024-11-10 02:02:10

我没有 100% 遵循你的问题,但这里是......

我唯一能想到的 #5 就是使 Command::execute 私有/受保护并使 Manager< /code> Command朋友。此方法的缺点是您现在引入了从 CommandManager 的依赖关系。

对于#6,如果用户的 shared_ptr 对象源自 Manager 的 shared_ptr 集合,则 Manager 应该能够安全地将 const_pointer_cast shared_ptr 转换回 shared_ptr。如果 Manager 尝试 const 强制转换 shared_ptr(其中指针对象是实际的常量对象),您将得到未定义的行为。


我想到了 #5 的另一个解决方案:

定义一个从 Command 派生的 ExecutableCommand 类。 ExecutableCommand 添加了一个调用命令的方法,仅由 Manager 使用。客户端只能通过 Command 的指针/引用来访问 ExecutableCommand 对象。当管理器想要调用Command时,它将其向下转换为ExecutableCommand以获得对调用接口的访问。

工作示例(包括 #6 的 const_pointer_cast):

#include <iostream>
#include <string>
#include <vector>
#include <boost/shared_ptr.hpp>

using namespace std;
using namespace boost;

//------------------------------------------------------------------------------
struct Foo
{
    Foo(int x) : x(x) {}
    void print() {++x; cout << "x = " << x << "\n";} // non-const
    int x;
};

//------------------------------------------------------------------------------
struct Command
{
    // Interface accessible to users
    std::string name;

private:
    virtual void execute() = 0;
};

//------------------------------------------------------------------------------
struct ExecutableCommand : public Command
{
    // Only accessible to Manager
    virtual void execute() {} // You may want to make this pure virtual
};

//------------------------------------------------------------------------------
struct PrintCommand : public ExecutableCommand
{
    PrintCommand(shared_ptr<const Foo> foo)
        : foo_( const_pointer_cast<Foo>(foo) ) {}

    void execute() {foo_->print();}

private:
    shared_ptr<Foo> foo_;
};

//------------------------------------------------------------------------------
struct Manager
{
    void execute(Command& command)
    {
        ExecutableCommand& ecmd = dynamic_cast<ExecutableCommand&>(command);
        ecmd.execute();
    }

    void addFoo(shared_ptr<Foo> foo) {fooVec.push_back(foo);}

    shared_ptr<const Foo> getFoo(size_t index) {return fooVec.at(index);}

private:
    std::vector< shared_ptr<Foo> > fooVec;
};

//------------------------------------------------------------------------------
int main()
{
    Manager mgr;
    mgr.addFoo( shared_ptr<Foo>(new Foo(41)) );

    Command* print = new PrintCommand(mgr.getFoo(0));
    // print.execute() // Not allowed
    mgr.execute(*print);

    delete print;
}

I'm not following your question 100%, but here goes...

The only thing I can think of for #5 is to make Command::execute private/protected and make Manager a friend of Command. The downside of this approach is that you've now introduced a dependency from Command to Manager.

As for #6, if the user's shared_ptr<const Foo> objects were originated from Manager's shared_ptr<Foo> collection, then Manager should be able to safely const_pointer_cast shared_ptr<const Foo*> back into shared_ptr<Foo*>. If Manager attempts to const cast a shared_ptr<const Foo*>, where the pointee is an actual constant object, you'll get undefined behavior.


I've thought of another solution for #5:

Define an ExecutableCommand class, derived from Command. ExecutableCommand has an added method to invoke the command, to be used only by Manager. Clients can only access ExecutableCommand objects via pointers/references to Command. When a Manager wants to invoke a Command, it downcasts it to a ExecutableCommand to gain access to the invocation interface.

Working example (including const_pointer_cast for #6):

#include <iostream>
#include <string>
#include <vector>
#include <boost/shared_ptr.hpp>

using namespace std;
using namespace boost;

//------------------------------------------------------------------------------
struct Foo
{
    Foo(int x) : x(x) {}
    void print() {++x; cout << "x = " << x << "\n";} // non-const
    int x;
};

//------------------------------------------------------------------------------
struct Command
{
    // Interface accessible to users
    std::string name;

private:
    virtual void execute() = 0;
};

//------------------------------------------------------------------------------
struct ExecutableCommand : public Command
{
    // Only accessible to Manager
    virtual void execute() {} // You may want to make this pure virtual
};

//------------------------------------------------------------------------------
struct PrintCommand : public ExecutableCommand
{
    PrintCommand(shared_ptr<const Foo> foo)
        : foo_( const_pointer_cast<Foo>(foo) ) {}

    void execute() {foo_->print();}

private:
    shared_ptr<Foo> foo_;
};

//------------------------------------------------------------------------------
struct Manager
{
    void execute(Command& command)
    {
        ExecutableCommand& ecmd = dynamic_cast<ExecutableCommand&>(command);
        ecmd.execute();
    }

    void addFoo(shared_ptr<Foo> foo) {fooVec.push_back(foo);}

    shared_ptr<const Foo> getFoo(size_t index) {return fooVec.at(index);}

private:
    std::vector< shared_ptr<Foo> > fooVec;
};

//------------------------------------------------------------------------------
int main()
{
    Manager mgr;
    mgr.addFoo( shared_ptr<Foo>(new Foo(41)) );

    Command* print = new PrintCommand(mgr.getFoo(0));
    // print.execute() // Not allowed
    mgr.execute(*print);

    delete print;
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文