C ++:如何获得解耦的多态行为 [英] C++: How to get decoupled polymorphic behavior

查看:103
本文介绍了C ++:如何获得解耦的多态行为的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在C ++中的Qt项目中,我正在使用QtPlugin为动态加载的插件编写接口.该界面应允许插件注册其不同的参数,并且在加载插件时,主程序应显示代表每个参数的适当的GUI控件.例如,参数可以是框中的QLabel和QSlider表示的0到20之间的int或QColorDialog表示的颜色值.

In a Qt project in C++, I am writing an interface for dynamically loaded plugins using QtPlugin. This interface should allow plugins to register their different parameters, and while a plugin is loaded the main program should display appropriate GUI controls representing each parameter. For instance, a parameter could be an int between 0 and 20 a represented by a QLabel and a QSlider in a box, or a color value represented by a QColorDialog.

这很重要:我尝试了一种标准的OOP方法(?),让每种参数类型都继承一个抽象类,并通过实现一个虚函数来创建GUI表示形式.这导致许多Qt GUI头链接到每个插件文件,其大小从〜20 KB增加到〜50 KB.

Here's the catch: I tried a standard OOP approach (?), having each parameter type inherit an abstract class and creating the GUI representation by implementing a virtual function. This caused a lot of Qt GUI headers to get linked into each plugin file, increasing its size from ~20 KB to ~50 KB.

这不是要节省那些千字节,而是要更好地理解OOP.我考虑了一下并试图找到合适的设计模式,然后我在解耦多态",外部多态"等位置进行了搜索,并遇到了一个页面,说这是可能的,但通常您不想去那里,因为它打破了OOP .
就是这样吗?我是从插件界面中隐藏GUI代码并用枚举之类的东西来标识每种类型,然后破坏OOP",还是该类完全对自己负责,而且还完全在内部耦合?

This isn't about saving those kilobytes but about gaining a better understanding of OOP. I thought about this and tried to find suitable design patterns, then I googled "decoupled polymorphism", "external polymorphism" et c and came across a page that said that this is possible but generally you don't want to go there because it breaks OOP.
So is that it? Either I hide GUI code from the plugin interface and identify each type with an enum or something, and "break OOP", or the class is entirely reponsible for itself but also completely coupled internally?

如果每种参数类型都包含一个数据模型,持久性和带有信号的GUI控件,那么您将建议什么解决方案?哪里去了?

What solutions would you recommend, if each parameter type consists of a data model, persistence, and GUI controls with signals? What goes where?

换句话说,我想知道插件是否可以是纯数据和算法,不知道如何在Qt中创建数据控件并且与Qt GUI头无关.它可能会使用Q_OBJECT作为信号.

In other words I'm wondering if the plugins can be pure data and algorithms, unaware of how data controls are created in Qt and independent of Qt GUI headers. It might use Q_OBJECT for signals though.

推荐答案

我建议让插件担心其参数的 types ,并拥有一个单独的组件,该组件知道如何映射每个参数在GUI控件上输入.

I'd suggest letting the plugin worry about the types of its arguments, and have a separate component which knows how to map each type onto a GUI control.

这几乎是一个简单的模型/视图分解,因此似乎是一个易于理解的习惯用法.

This is almost a straight model/view decomposition, so seems like a well-understood idiom.

现在,您的 type 模型可以枚举,或者可以使用可以说是更多的OO Visitor模式,但是实际上您仍然会提出一个固定的且不是很可扩展的类型系统时间.够了吗?

Now, your type model can be enumerated, or you can use the arguably more OO Visitor pattern, but you're still essentially coming up with a fixed and not-really-extensible type system ahead of time. Is that adequate?

您可能会得到某种类型的类型,该类型既知道给定参数的特定派生类型,又知道如何在Qt中呈现它的详细信息.这将处理Qt信号,并将值传递回参数.

You'll probably end up with some type that knows both the specific derived type of a given argument, and the details of how to render it in Qt. This would handle the Qt signals, and pass values back to the argument.

...通过尝试dynamic_cast或读取某种识别代码(例如枚举),我正在思考.我仍然看不到如何使用Visitor DP来代替这些...

... Through attempting a dynamic_cast or reading some kind of identification code such as an enum, I'm thinking. I still don't see how the Visitor DP could be used instead of these ...

专门用于 的Visitor模式是为了避免dynamic_cast,所以我不确定这里的混乱之处.诚然,有一个确实使用dynamic_cast的事后版本,但该实现隐藏在实现中,反正也不是通常的情况.

The Visitor pattern is specifically used to avoid dynamic_cast, so I'm not sure what the confusion is here. Admittedly there's a post-hoc version which does use dynamic_cast, but that's hidden away in the implementation and isn't the usual case anyway.

因此,举一个具体的例子,让我们创建一个带有几个参数类型的模型:

So, for a concrete example, let's create a model with a couple of argument types:

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...
};

现在我们可以访问一些具体类型,我们可以编写抽象访问者

Now we have some concrete types for it to visit, we can write the abstract visitor

struct ArgumentHandler {
    virtual ~ArgumentHandler() {}

    virtual void handleInteger(IntegerArgument&);
    virtual void handleBoundedInteger(BoundedIntegerArgument&);
    // ...
};

和我们的具体类型实现访问,如下所示:

and our concrete types implement visitation like so:

void IntegerArgument::visit(ArgumentHandler& handler) {
    hander.handleInteger(*this);
}

void BoundedIntegerArgument::visit(ArgumentHandler& handler) {
    hander.handleBoundedInteger(*this);
}

现在,我们只能根据数据模型类型编写一个抽象插件-它不需要了解GUI工具包的任何知识.假设我们只是提供一种查询其参数的方法(请注意,每个具体的子类型都应具有set/get方法)

Now, we can write an abstract plugin only in terms of the data model types - it doesn't need to know anything about the GUI toolkit. Let's say we just provide a way to query its arguments for now (note that each concrete subtype should have set/get methods)

class PluginBase
{
public:
    virtual int arg_count() const =  0;
    virtual Argument& arg(int n) =  0;
};

最后,我们可以绘制一个视图,该视图知道如何询问抽象插件的参数,如何显示每种具体参数类型以及如何处理输入:

Finally, we can sketch a View that knows how to interrogate an abstract plugin for its arguments, how to display each concrete argument type, and how handle inputs:

// 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对象如何处理其俘获的Spinbox小部件中的valueChanged信号,并调用model_.set()将其推回插件.

I've omitted all the virtual destructors, the Qt signal handling, and lots more. But, hopefully you can see how a QtView::IntegerSpinBox object could handle the valueChanged signal from its captive spinbox widget, and call model_.set() to push that back to the plugin.

这篇关于C ++:如何获得解耦的多态行为的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆