更新时间:2023-02-14 23:28:34
我终于决定使用 boost :: variant 在我的列表
class。我的属性
类现在是一个模板类,其中模板参数用 ValueType
类的子类实例化。 属性
的不同口味来源于属性
类。
现在看来,这种方法似乎满足了我所有的初始要求:
列表
中的所有属性
,并调用属性
可以使用 std :: for_each 和 boost :: apply_visitor ✓ Property
的实例时,如果我的属性
基类允许访问其 ValueType
成员。 ✓对此方法的任何评论仍然赞赏。
I'm writing a DAL/ORM library. This library will be accessed mainly from GUIs but also from some "business level" applications. I'm still in the design phase of this library and came to a point where I'm not sure how to solve the following issue nicely.
In my current design I have a class, let's call it List
for the moment, that has a container of another class, Properties
. Properties come in two flavors (A and B), with mostly the same functionality, but some of their functionality is different. Furthermore both flavors of Properties
store values. Values can be of different data types, including, but not limited to, PODs. Each List
can contain a certain Property
only once and Properties
are identified by a "name", i.e. a string.
I now want to be able to do all of the following:
Properties
in List
, and call methods that both Property
flavors support.Property
, accessing its value in a type safe waydynamic_cast
or similar constructsSo, obviously pure polymorphism cannot do the trick here. I have done some experiments with the curiously recurring template pattern and with composition of two class hierarchies - one for Properties
and one for their values (example code below). However, so far I did not succeed in getting a design that fulfilled all my requirements.
The basic design (i.e. which classes exist, how they are organized etc.) is not fixed and could be easily changed. I am still in the design phase of this project, so only test code exists. However, the basic idea has to bee like explained above (i.e. that a List
has Properties
which in turn have values
).
Any solutions to my problems or raw ideas, thoughts, etc. are highly appreciated.
Example code for hierarchy implementation. Obviously I will not be able to access a property's value in a type-safe way here.
class PropertyValue {
public:
virtual std::string GetAsString() const = 0;
bool IsReadOnly() const { return m_isReadOnly; }
void IsReadOnly(const bool val) { m_isReadOnly = val; }
protected:
PropertyValue(PropertyValue & other) : m_isReadOnly(other.m_isReadOnly)
{};
PropertyValue(bool readOnly) : m_isReadOnly(readOnly)
{};
private:
bool m_isReadOnly;
};
class StringValue : public PropertyValue {
private:
typedef std::string inner_type;
public:
StringValue(const inner_type & value, bool readOnly) : PropertyValue(readOnly)
, m_value(value)
{};
StringValue(StringValue & other) : PropertyValue(other.IsReadOnly())
, m_value(other.m_value)
{};
std::string GetAsString() const { return m_value; };
inner_type GetValue() const { return m_value; };
void SetValue(const inner_type & value) { m_value = value; };
unsigned int GetMaxLenght() const { return m_maxLength; };
private:
inner_type m_value;
unsigned int m_maxLength;
};
class IntValue : public PropertyValue {
private:
typedef int inner_type;
public:
IntValue(const inner_type & value, bool readOnly) : PropertyValue(readOnly)
, m_value(value)
{};
IntValue(IntValue & other) : PropertyValue(other.IsReadOnly())
, m_value(other.m_value)
{};
std::string GetAsString() const { char tmp[((CHAR_BIT * sizeof(int)) / 3 + 1)]; return itoa(m_value, tmp, 10); };
inner_type GetValue() const { return m_value; };
void SetValue(const inner_type & value) { m_value = value; };
int GetMinValue() const { return m_minValue; };
int GetMaxValue() const { return m_maxValue; };
private:
inner_type m_value;
int m_minValue;
int m_maxValue;
};
class Property {
public:
Property(std::auto_ptr<PropertyValue> value, bool visible)
{
m_value = value;
m_isVisible = visible;
}
bool IsVisible() const { return m_isVisible; }
void IsVisible(const bool val) { m_isVisible = val; }
std::string GetValueAsString() const { return m_value->GetAsString(); };
const PropertyValue & getValue() const { return (*m_value.get()); }
private:
std::auto_ptr<PropertyValue> m_value;
bool m_isVisible;
};
class PropertyFlavorA : public Property {
public:
PropertyFlavorA(std::auto_ptr<PropertyValue> value, bool visible) : Property(value, visible)
{
value->IsReadOnly(true);
};
};
class PropertyFlavorB : public Property {
public:
PropertyFlavorB(std::auto_ptr<PropertyValue> value, bool visible) : Property(value, visible) {};
};
I finally decided to use a vector of boost::variant in my List
class. My Property
class is now a template class where the template parameter is instantiated with subclasses of a ValueType
class. The different flavors of Properties
derive from the Property
class.
Right now it seems that this approach fulfills all my initial requirements:
Properties
in List
, and calling methods that both Property
flavors support is possible by using std::for_each and boost::apply_visitor ✓ Property
, accessing its value in a type safe way is possible if my Property
base class gives access to its ValueType
member. ✓ Any comments on this approach are still appreciated.