0

C++ の Qt プロジェクトで、QtPlugin を使用して動的に読み込まれるプラグインのインターフェイスを作成しています。このインターフェースは、プラグインがそれぞれのパラメータを登録できるようにする必要があり、プラグインがロードされている間、メイン プログラムは各パラメータを表す適切な GUI コントロールを表示する必要があります。たとえば、パラメーターは、ボックス内の QLabel と QSlider で表される 0 ~ 20 の int、または QColorDialog で表される色の値である可能性があります。

問題は次のとおりです。標準の OOP アプローチ (?) を試しました。各パラメーターの型に抽象クラスを継承させ、仮想関数を実装して GUI 表現を作成しました。これにより、多くの Qt GUI ヘッダーが各プラグイン ファイルにリンクされ、サイズが ~20 KB から ~50 KB に増加しました。

これはキロバイトを節約することではなく、OOP をよりよく理解することです。私はこれについて考え、適切な設計パターンを見つけようとしましたが、「デカップリングされたポリモーフィズム」、「外部ポリモーフィズム」などをグーグルで検索し、これは可能であると述べたページに出くわしましたが、一般的には壊れるのでそこに行きたくありませんおっと
それで、それですか?プラグイン インターフェイスから GUI コードを非表示にして、列挙型などで各タイプを識別し、「OOP を解除」するか、クラスが完全に責任を負うだけでなく、内部的にも完全に結合されますか?

各パラメーター タイプがデータ モデル、永続性、およびシグナルを含む GUI コントロールで構成されている場合、どのようなソリューションをお勧めしますか? 何がどこに行くの?

編集: 言い換えれば、Qt でデータ コントロールがどのように作成され、Qt GUI ヘッダーから独立しているかを認識せずに、プラグインが純粋なデータとアルゴリズムである可能性があるかどうか疑問に思っています。ただし、シグナルに Q_OBJECT を使用する場合があります。

4

3 に答える 3

3

プラグインにその引数の型を心配させ、各型を GUI コントロールにマップする方法を知っている別のコンポーネントを用意することをお勧めします。

これはほぼ単純なモデル/ビューの分解であるため、よく理解されているイディオムのようです。

これで、モデルを列挙することができます。または、おそらくより多くの OO Visitor パターンを使用することもできますが、基本的には、固定された実際には拡張できない型システムを前もって考え出すことになります。それで十分ですか?

おそらく、特定の引数の特定の派生型と Qt でのレンダリング方法の詳細の両方を知っている型になるでしょう。これは Qt シグナルを処理し、値を引数に戻します。


... dynamic_cast を試したり、enum などの識別コードを読み取ったりして、考えています。これらの代わりにビジター DP をどのように使用できるかはまだわかりません...

Visitor パターンはを回避するために特に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;
};

最後に、抽象プラグインにその引数を問い合わせる方法、各具体的な引数の型を表示する方法、および入力を処理する方法を知っている View をスケッチできます。

// 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そのキャプティブ スピンボックス ウィジェットからのシグナルをどのように処理し、model_.set()それをプラグインにプッシュするために呼び出すことができるかがわかります。

于 2013-03-30T20:12:41.747 に答える
1

任意のタイプのメッセージをどこにでも送信し、何とでも疎結合する目的のために正確に作成された一時的な仮想パックを使用して、反対側でそれをキャッチできます。

于 2015-06-12T07:28:08.727 に答える
0

私があなたのことを正しく理解していれば、あなたはその行動を考え直すべきです。モジュールがすべてを登録する代わりに (これは非常に多くの場合があります)、モジュール固有のレンダラーの基本クラスと、モジュールの具体的なレンダラーをインスタンス化する各モジュールのファクトリを作成できます。次に、モジュールに提供する情報をレンダリングするようにモジュールに依頼できます。

于 2013-03-30T19:01:31.753 に答える