楽しみのために、私は 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 クラスは、インターフェイスのみを提供します。だから、それはもっとできる関係です。