Я пишу библиотеку DAL/ORM. Доступ к этой библиотеке будет осуществляться в основном из графических интерфейсов, но также и из некоторых приложений «бизнес-уровня». Я все еще нахожусь на этапе проектирования этой библиотеки и дошел до того, что не знаю, как красиво решить следующую проблему.
В моем текущем дизайне у меня есть класс, назовем его пока List
, у которого есть контейнер другого класса, Properties
. Свойства бывают двух видов (A и B), в основном с одинаковыми функциями, но некоторые их функции различаются. Кроме того, обе разновидности Properties
хранят значения. Значения могут относиться к разным типам данных, включая, помимо прочего, POD. Каждый List
может содержать определенный Property
только один раз, а Properties
идентифицируются "именем", то есть строкой.
Теперь я хочу иметь возможность делать все следующее:
- Держите сложность интерфейса как можно ниже
- Переберите все
Properties
вList
и вызовите методы, которые поддерживают оба вариантаProperty
. - При наличии экземпляра
Property
доступ к его значению безопасным способом - Если возможно, избегайте
dynamic_cast
или подобных конструкций.
Таким образом, очевидно, что чистый полиморфизм здесь не поможет. Я провел несколько экспериментов с любопытно повторяющимся шаблоном шаблона и с композицией двух иерархий классов — одной для Properties
и одной для их значений (пример кода ниже). Однако пока мне не удалось получить дизайн, удовлетворяющий всем моим требованиям.
Базовый дизайн (т. е. какие классы существуют, как они организованы и т. д.) не является фиксированным и может быть легко изменен. Я все еще нахожусь на стадии разработки этого проекта, поэтому существует только тестовый код. Однако основная идея должна быть такой, как описано выше (то есть List
имеет Properties
, которые, в свою очередь, имеют values
).
Любые решения моих проблем или необработанные идеи, мысли и т. д. высоко ценятся.
Пример кода для реализации иерархии. Очевидно, что здесь я не смогу получить доступ к значению свойства безопасным для типов способом.
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) {};
};