我建议让插件担心其参数的类型,并有一个单独的组件知道如何将每种类型映射到 GUI 控件上。
这几乎是一个直接的模型/视图分解,所以似乎是一个很好理解的习语。
现在,您的类型模型可以被枚举,或者您可以使用可以说是更多的 OO 访问者模式,但是您基本上仍然提前提出了一个固定且不可扩展的类型系统。这够吗?
您最终可能会得到某种类型,该类型既知道给定参数的特定派生类型,又知道如何在 Qt 中呈现它的详细信息。这将处理 Qt 信号,并将值传递回参数。
...通过尝试 dynamic_cast 或阅读某种识别代码(例如枚举),我在想。我仍然不明白如何使用访客 DP 来代替这些......
访问者模式专门用于避免dynamic_cast
,所以我不确定这里有什么混淆。诚然,有一个事后版本确实使用dynamic_cast
了 ,但它隐藏在实现中,无论如何都不是常见的情况。
因此,举一个具体的例子,让我们创建一个具有几个参数类型的模型:
struct ArgumentHandler; // visitor
class Argument { // base class for visitable concrete types
public:
virtual void visit(ArgumentHandler&) = 0;
};
// sample concrete types
class IntegerArgument: public Argument {
int value_;
public:
IntegerArgument(int value = 0) : value_(value) {}
void set(int v) { value_ = v; }
int get() const { return value_; }
virtual void visit(ArgumentHandler&);
};
class BoundedIntegerArgument: public IntegerArgument
{
int min_, max_;
public:
virtual void visit(ArgumentHandler&);
// etc...
};
现在我们有了一些具体的类型供它访问,我们可以写抽象的访问者
struct ArgumentHandler {
virtual ~ArgumentHandler() {}
virtual void handleInteger(IntegerArgument&);
virtual void handleBoundedInteger(BoundedIntegerArgument&);
// ...
};
我们的具体类型像这样实现访问:
void IntegerArgument::visit(ArgumentHandler& handler) {
hander.handleInteger(*this);
}
void BoundedIntegerArgument::visit(ArgumentHandler& handler) {
hander.handleBoundedInteger(*this);
}
现在,我们可以只根据数据模型类型编写一个抽象插件——它不需要了解有关 GUI 工具包的任何信息。假设我们现在只提供一种查询其参数的方法(注意每个具体的子类型都应该有 set/get 方法)
class PluginBase
{
public:
virtual int arg_count() const = 0;
virtual Argument& arg(int n) = 0;
};
最后,我们可以绘制一个视图,它知道如何查询抽象插件的参数、如何显示每个具体的参数类型以及如何处理输入:
// concrete renderer
class QtView: public ArgumentHandler
{
struct Control {};
struct IntegerSpinBox: public Control {
QSpinBox control_;
IntegerArgument &model_;
};
struct IntegerSlider: public Control {
QSlider control_;
BoundedIntegerArgument &model_;
};
std::vector<std::unique_ptr<Control>> controls_;
public:
// these overloads know how to render each argument type
virtual void handleInteger(IntegerArgument &arg) {
controls_.push_back(new IntegerSpinBox(arg));
}
virtual void handleBoundedInteger(BoundedIntegerArgument &arg) {
controls_.push_back(new IntegerSlider(arg));
}
// and this is how we invoke them:
explicit QtView(PluginBase &plugin) {
for (int i=0; i < plugin.arg_count(); ++i) {
plugin.arg(i).visit(*this);
}
}
};
我省略了所有的虚拟析构函数、Qt 信号处理等等。但是,希望您能看到一个QtView::IntegerSpinBox
对象如何处理valueChanged
来自其捕获的 spinbox 小部件的信号,并调用model_.set()
将其推回插件。