2

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

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

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

我想遵循以下规则:

  1. 用户(客户端代码)有一些对象,称之为“经理”
  2. Manager拥有一系列shared_ptr<Foo>
  3. Manager通过返回提供对集合的访问shared_ptr<const Foo>
  4. 我有一个Command抽象类和一个命令层次结构,用于执行操作Foo
  5. 客户端代码不能(没有变通方法)调用Command::execute(),只能Manager 调用Manager::execute(shared_ptr<Command>)以便它可以处理撤消/重做并获取非常量智能指针
  6. AManager必须能够允许Command对象访问和修改shared_ptr<Foo>,即使用户Command使用shared_ptr<const Foo>

我只是想找出shared_ptr<const Foo>在允许 5 号和 6 号工作的同时处理捐赠的最佳方式。

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

4

3 回答 3

2

我认为这passkey pattern对你来说应该是正确的:

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功能所需的密钥。用户将无法execute直接调用该方法,因为只能Manager创建CommandExecuteKey对象,这要归功于私有构造函数和friend船舶。

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

现在,对于您的第 6 点:
当您输入命令时,在您的所有shared_ptr<Foo>s 中搜索正确的对象(使用保存shared_ptr的命令作为搜索键),然后将该可变对象从您的内部shared_ptrs 传递回命令。

于 2011-04-22T18:42:57.333 回答
1

我没有100%关注你的问题,但是这里......

对于#5 ,我唯一能想到的就是将Command::execute私有/受保护Managerfriend. Command这种方法的缺点是您现在已经引入了从Commandto的依赖关系Manager

至于 #6,如果用户的shared_ptr<const Foo>对象来自 Manager 的shared_ptr<Foo>集合,那么Manager 应该能够安全地将 const_pointer_castshared_ptr<const Foo*>重新转换为shared_ptr<Foo*>. 如果 Manager 尝试 const cast a shared_ptr<const Foo*>,其中指针对象是实际的常量对象,您将获得未定义的行为。


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

定义一个ExecutableCommand类,派生自Command. ExecutableCommand有一个调用命令的附加方法,仅供Manager. 客户端只能ExecutableCommand通过指针/引用访问对象Command。当 Manager 想要调用 aCommand时,它会将其向下转换为 aExecutableCommand以获得对调用接口的访问权限。

工作示例(包括#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;
}
于 2011-04-22T18:10:39.293 回答
1

由于否则对我没有任何意义,我将假设

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

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

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
}

我不确定 usingconst是否是这里的最佳解决方案。也许您应该将Foo对象包装在例如ClientFoo对象中。经理只向ClientFoo. 然后,管理器可以(例如通过friendFoo从 中获取ClientFoo,并使用它来调用Command

于 2011-04-22T18:31:22.633 回答