gpt4 book ai didi

c++ - 寻找设计模式以减少虚拟方法的重载

转载 作者:行者123 更新时间:2023-12-01 19:29:16 25 4
gpt4 key购买 nike

我有大量的(〜100)类是从一个通用基类(Device)派生的。每个设备都可以接受大量类似命令的某些子集。不同的命令可以具有不同数量和类型的参数,因此每个命令都按其自己的类型封装(如有必要,可以更改)。

在仅给Device基类提供指针/引用的情况下,有什么模式可以使我将命令传递给设备,以便设备可以访问命令的类型和参数?

我提出的选项:

  • 最直接的方法是在基类Device中添加一个接受每种命令类型的单独虚拟方法。但是,这最终将在基类中使用大量虚方法,而这些虚方法仅在很少的派生类中被覆盖。
  • 我考虑了访问者模式,但是由于命令类的数量大约等于设备类的数量,因此这实际上并没有获得任何好处。
  • 使用RTTI(或每个命令唯一的枚举/标识符)确定命令类型,然后使用switch / if分支到适当的代码。这感觉很脏,因为它绕过了常规的C++多态性。另外,dynamic_cast在这里非常令人讨厌,因此几乎没有这个选项。

  • 对于在 Device基类中没有大量虚拟方法的情况下,可以干净利落地使用此模式的任何建议?

    最佳答案

    这是经典的double dispatch problem

    我已经遇到过这种模式几次,并使用以下策略来处理它。

    假设基类Command具有一个返回“id”的函数,该ID可以是整数类型,字符串类型,也可以用作映射中的键。

    struct Command
    {
    typedef <SomeType> IDType;
    virtual IDType getID() const = 0;
    };
    Device的接口(interface)可以简化为:
    struct Command;
    struct Device
    {
    virtual execute(Command const& command) = 0;
    };

    假设 DeviceABCD是派生类型之一,并且通过基类指针/引用操作的实际设备是 DeviceABCD。在第一次调度中,将执行命令的调用调度到 DeviceABCD::execute()
    DeviceABCD::execute()的实现将其分派(dispatch)给另一个执行实际工作的函数。

    您需要一个适当的框架来正确执行第二次调度。在框架中:
  • 需要有“命令ID”->“命令执行器”的映射。
  • 在给定“命令ID”的情况下,需要一种方法来注册“命令执行器”。

  • 基于这些,您可以在给定“命令ID”的情况下获得“命令执行器”。如果存在“命令执行器”,则可以简单地将命令执行分派(dispatch)到“命令执行器”。如果不是,则需要处理错误,最有可能通过引发异常来处理。

    该框架可用于 Device的所有子类型。因此,可以使用 Device本身或与 Device对等的帮助程序类来实现该框架。我更喜欢第二种方法,建议创建几个类: CommandExecutorCommandDispatcher

    CommandExecutor.h:
    struct CommandExecutor
    {
    virtual execute(Command const& command) = 0;
    };

    CommandDispatcher.h:
    class CommandDispatcher
    {
    public:
    void registerCommandExecutor(Command::IDType commandID,
    CommandExecutor* executor);

    void executeCommand(Command const& command);

    std::map<Command::IDType, CommandExecutor*>& getCommandExecutorMap();

    public:
    std::map<Command::IDType, CommandExecutor*> theMap;
    };

    CommandDispatcher.cpp:
    void CommandDispatcher::registerCommandExecutor(Command::IDType commandID,
    CommandExecutor* executor)
    {
    getCommandExecutorMap()[commandID] = executor;
    }

    void CommandDispatcher::executeCommand(Command const& command)
    {
    CommandExecutor* executor = getCommandExecutorMap()[commandID];
    if ( executor != nullptr )
    {
    executor->execute(command);
    }
    else
    {
    throw <AnAppropriateExecption>;
    }
    }

    std::map<Command::IDType, CommandExecutor*>& CommandDispatcher::getCommandExecutorMap()
    {
    return theMap;
    }

    如果 DeviceABCD可以执行 Command12Command34,则其实现将类似于:

    DeviceABCD.cpp:
    struct Command12Executor : public CommandExecutor
    {
    virtual void execute(Command const& command) { ... }
    };

    struct Command34Executor : public CommandExecutor
    {
    virtual void execute(Command const& command) { ... }
    };

    DeviceABCD::DeviceABCD() : commandDispatcher_(CommandExecutor)
    {
    static Command12Executor executor12;
    static Command34Executor executor34;

    // This assumes that you can get an ID for all instances of Command12
    // without an instance of the class, i.e. it is static data of the class.

    commandDispatcher_.registerExecutor(Command12Type, &executor12);
    commandDispatcher_.registerExecutor(Command34Type, &executor34);
    }

    有了该框架, DeviceABCD::execute()的实现就非常简单。
    void DeviceABCD::execute(Command const& command)
    {
    commandDispatcher_.executeCommand(command);
    }

    精简到可以在基类中实现的程度。仅当需要在命令被分派(dispatch)到正确的CommandExecutor之前对命令进行按摩或更新某些其他状态时,才需要在派生类中实现它。

    关于c++ - 寻找设计模式以减少虚拟方法的重载,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33613155/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com