命令模式 - 使用"weight"执行任务的命令

Command pattern - Commands that execute the task with a "weight"

本文关键字:命令 执行任务 weight 使用 模式      更新时间:2023-10-16

我目前正在为未来的项目设计一个基础,更具体地说,我正在研究输入处理。我正在使用命令模式来处理输入,在创建输入上下文时,程序员可以通过调用程序将命令绑定到键或鼠标按钮,该调用程序根据应用程序的不同条件、按键、鼠标在窗口上的位置等执行命令。

当我在禁用光标的输入上下文中(例如在控制 3D 相机时(添加鼠标处理时,我遇到了麻烦(这实际上是我能想到的唯一情况(。

我认为这是有效的,程序员绑定一个命令,一个旋转相机的命令,一旦创建描述鼠标移动的事件就会激活。该命令将保存指向相机对象的指针,并在执行时调用类似camera->pan()的函数。当鼠标在 X 轴上移动时,将执行此命令。但是,如果是这种情况,无论鼠标移动的速度有多快或多慢,相机都将始终以恒定的速度平移。如果相机函数pan()具有用于指定平移量的参数,则Command对象在执行时需要具有此参数的值。如果在创建命令时指定该值并将其存储为成员,则问题将再次出现,因为每次调用函数时,参数都将具有相同的值。

我建议的这个问题的解决方案是简单地创建Command类的变体,称为类似WeightedCommand的变体,其execute()函数中有一个参数。此参数将是传递给相机pan()功能的"权重"。这将允许每次调用命令时都以不同的"权重"执行命令,或者相同的"权重",由程序员决定。

作为参考,这是来自维基百科的命令模式的示例。

class Light {
public:
void TurnOn() { std::cout << "The light is on." << std::endl; }
void TurnOff() { std::cout << "The light is off." << std::endl; }
};

class ICommand {
public:
virtual ~ICommand() = default;
virtual void Execute() = 0;
};
// The Command for turning on the light - ConcreteCommand #1
class FlipUpCommand : public ICommand {
public:
FlipUpCommand(Light* light) : light_(light) { assert(light_); }
void Execute() { light_->TurnOn(); }
private:
Light* light_;
};

加权命令的示例:

class WeightedCommand
{
public:
virtual ~WeightedCommand() = default;
virtual void execute(double weight) = 0;
};
class PanCamera : public WeightedCommand
{
public:
PanCamer(Camera* cam)
: _camera(cam;
{}
void execute(double weight)
{
_camera->pan(weight);
}    
private:
Camera* _camera;
};

你能看到这种方法的任何缺陷吗?是否有更好的解决方案?我尝试寻找类似问题的解决方案,但找不到真正合适的方法。

这是一个基于意见的问题,但这里有一些建议。

你可以保留你的方法,并通过ICommand模板来概括它。

template <typename ...Args>
class ICommand 
{
public:
virtual ~ICommand() = default;
virtual void Execute(Args const& ...args) = 0;
};
class PanCamera : public ICommand<double>
{
void Execute(double const& pan) override
{
_camera->pan(pan);
}    
};

如果要将命令存储在容器中,则需要一个通用类型,该类型不适用于上面的示例。为避免这种情况,您可以将double参数替换为您已经提到的std::variant

using CommandArgs = std::variant<double, std::string>;
class PanCamera : public ICommand<CommandArgs>
{
void Execute(CommandArgs const& args) override
{
_camera->pan(std::get<double>(args));
}    
};
class SayHello : public ICommand<CommandArgs>
{
void Execute(CommandArgs const& args) override
{
display->sayHello(std::get<std::string>(args));
}    
};

您还可以完全放弃ICommand界面,并使用访问者模式进行std::variant

struct PanCameraArgs
{
double value = 0;
};
struct SayHelloArgs
{
std::string text;
};
struct RotateCameraArgs
{
double angle = 0;
};
using CommandArgs = std::variant<PanCameraArgs, SayHelloArgs, RotateCameraArgs>;
void dispatchCommand(CommandArgs const& command)
{
std::visit( overloaded {
[&] (PanCameraArgs const& args)
{
_camera->pan(pan.value);
}
[&] (SayHelloArgs const& args)
{
display->sayHello(args.text);
}
[&] (RotateCameraArgs const& args)
{
_camera->rotate(args.angle);
}
}, command);
}