1

楽しみのために、私は Windows 用の XUL 実装に取り​​組んでいます。XUL では、UI 要素は次のように XML で記述できます。

<window width="800" height="600"></window>

要素の属性を取得および設定するためのシステムを検討しています。それはかなりうまくいっていますが、ここでダイヤモンド継承の使用が潜在的に危険であるかどうかはわかりません. 以下に完全なコードサンプルを投稿しました。

#include <boost/lexical_cast.hpp>
#include <string>
#include <map>


class Attribute
{
public:
    virtual void get(std::string & outValue) = 0;
    virtual void set(const std::string & inValue) = 0;

    static int String2Int(const std::string & inString)
    {
        return boost::lexical_cast<int>(inString);
    }

    static std::string Int2String(int inValue)
    {
        return boost::lexical_cast<std::string>(inValue);
    }
};


class Width : public Attribute
{
public:
    Width(){}

    virtual void get(std::string & outValue)
    {
        outValue = Int2String(getWidth());
    }

    virtual void set(const std::string & inValue)
    {
        setWidth(String2Int(inValue));
    }

    virtual int getWidth() const = 0;

    virtual void setWidth(int inWidth) = 0;
};


class Height : public Attribute
{
public:
    Height(){}

    virtual void get(std::string & outValue)
    {
        outValue = Int2String(getHeight());
    }

    virtual void set(const std::string & inValue)
    {
        setHeight(String2Int(inValue));
    }

    virtual int getHeight() const = 0;

    virtual void setHeight(int inHeight) = 0;
};

class Element : public Width,  // concerning the is-a vs has-a philosophy
                public Height  //   => see my note below
{
public:
    Element() :
        mWidth(0),
        mHeight(0)
    {
        // STATIC CAST NEEDED HERE OTHERWISE WE GET COMPILER ERROR:
        // error C2594: '=' : ambiguous conversions from 'Element *const ' to 'Attribute *'
        mAttrControllers["width"] = static_cast<Width*>(this);
        mAttrControllers["height"] = static_cast<Height*>(this);
    }

    void setAttribute(const std::string & inAttrName, const std::string & inAttrValue)
    {
        Attributes::iterator it = mAttrControllers.find(inAttrName);
        if (it != mAttrControllers.end())
        {
            Attribute * attribute = it->second;
            attribute->set(inAttrValue);
        }
    }

    std::string getAttribute(const std::string & inAttrName)
    {
        std::string result;
        Attributes::iterator it = mAttrControllers.find(inAttrName);
        if (it != mAttrControllers.end())
        {
            Attribute * attribute = it->second;
            attribute->get(result);
        }
        return result;
    }

    virtual int getWidth() const
    {
        return mWidth;
    }

    virtual void setWidth(int inWidth)
    {
        mWidth = inWidth;
    }

    virtual int getHeight() const
    {
        return mHeight;
    }

    virtual void setHeight(int inHeight)
    {
        mHeight = inHeight;
    }

private:
    typedef std::map<std::string, Attribute *> Attributes;
    Attributes mAttrControllers;
    int mWidth;
    int mHeight;
};


int main()
{
    Element el;
    el.setAttribute("width", "800");
    el.setAttribute("height", "600");
    int w = el.getWidth();
    int h = el.getHeight();
    return 0;
}

基本クラスの Attributes にはデータ メンバーがないため、競合は発生しないので問題ないと思います。しかし、コミュニティに確認してみようと思いました。あなたの洞察は大歓迎です!

編集 「is-a」と「has-a」、および「継承よりも構成を優先する」という発言について、私はこれを言いたい:

  • ここに継承の利点があります。Element が Width を継承する場合、getWidth メソッドと setWidth メソッドを実装する必要があります。したがって、属性を追加すると、Element のインターフェイスが「自動的に」更新されます。
  • これらのクラスにはもともと AttributeController、WidthController、HeightController という名前を付けていましたが、冗長すぎることがわかりました。私の要素は属性コントローラーであると言えます。(わかりました、それは不自由ですが、真実ではありません!)
  • さらなる証拠: Width と Height の定義にはデータ メンバーが含まれていません。Element クラスには実際それらがあります。Width クラスと Height クラスは、インターフェイスのみを提供します。だから、それはもっとできる関係です。
4

4 に答える 4

6

あなたのシナリオでは、Element はおそらく Width と Height から継承すべきではありませんが、代わりに、Width と Height は要素のデータ メンバーである必要があります。間違いなく要素は幅や高さではなく、幅と高さ (およびおそらく他のもの) で構成されているため、is-a とは対照的に構成です。

于 2009-09-27T16:40:53.483 に答える
2

ElementオブジェクトをWidthオブジェクトとして使用する必要がある場合にのみ、 ElementはWidth を継承する必要があります。継承はコードを再利用するためのものではありません

boost::program_options ライブラリを調べる価値があるかもしれません。プロパティを登録するための凝った方法が気に入っています。

于 2009-09-27T21:16:20.027 に答える
0

これを行う場合は、Attribute で仮想継承を行います。それほど重要ではないと思いますが、問題になった場合でも重複を最小限に抑えることができます。

于 2009-09-27T16:41:24.980 に答える
-1

まず、ひし形の問題に直面している場合は、仮想継承を使用します。つまり、基本クラスpublic virtualだけでなく使用します。public

第二に、 2 つの実装があるため、 get- と -が何をするかが明確に定義されていません。set多重継承を使用するのは、純粋仮想クラス (別名インターフェース) を拡張するときだけです。Java が多重継承をサポートしないのは十分な理由です。

第 3 に、さらに重要なことに、これはオブジェクト指向における継承 (「is a」) と集約 (「has a」) の誤解の間の典型的なケースのように思えます。クラスが別のクラスを継承する必要があるかどうかを判断するときは、2 つの非常に簡単なガイドラインを使用できます。クラス A とクラス A を継承するクラス B がある場合、「A は B です」という文は理にかなっているはずです。"A has a B" の方が適切な場合は、代わりに B を A のメンバーにすることを検討する必要があります。

あなたの場合、「要素は高さです」と「要素は幅です」という文は本当に意味がありません。「要素には高さがあります」は完全に理にかなっています。

于 2009-09-27T16:51:05.930 に答える