Альтернатива разработки для доступа к члену производного класса из указателя базового класса

Я пишу библиотеку 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) {};
};

person sigy    schedule 18.02.2014    source источник
comment
Реальный вопрос заключается в том, как вы хотите получить доступ к значениям. Вы просто выставляете их в графическом интерфейсе? Когда у вас есть ключ, знаете ли вы тип значения?   -  person IdeaHat    schedule 18.02.2014
comment
Я пишу DAL/ORM. Так что да, графический интерфейс — это основной домен, который будет использовать мою библиотеку. У меня нет настоящих пар ключ-значение. Однако я могу указать тип данных значений из какого-то ключа   -  person sigy    schedule 18.02.2014


Ответы (2)


Вы вроде сами ответили на свой вопрос...

Переберите все Properties в List и вызовите методы, которые поддерживают оба варианта Property.

Это полиморфизм.

Оба ваших варианта Properties имеют базовый класс с виртуальными функциями, которые переопределяют ваши классы A и B Property. Затем вы можете вызвать функцию из указателя базового класса, хранящегося в файле List.

person parrowdice    schedule 18.02.2014
comment
Я знаком с полиморфизмом. Но полиморфизм не позволяет мне получить доступ к значениям свойства типобезопасным способом. Помните, что они бывают разных типов. даже один и тот же вид свойства может иметь разные типы значений. - person sigy; 18.02.2014
comment
@sigy: Достаточно честно, можете ли вы привести пример своего Property (я полагаю, Класс?). Можете ли вы сделать свой класс Property похожим на тип данных Variant? - person parrowdice; 19.02.2014

В конце концов я решил использовать вектор boost::variant в моем List классе. Мой класс Property теперь является классом-шаблоном, в котором параметр шаблона создается с помощью подклассов класса ValueType. Различные варианты Properties происходят от класса Property.

Сейчас кажется, что этот подход удовлетворяет всем моим первоначальным требованиям:

  • Это снижает сложность интерфейса ✓
  • Перебор всех Properties в List и вызов методов, поддерживаемых обеими Property разновидностями, возможен с помощью std::for_each и boost:: apply_visitor
  • При наличии экземпляра Property доступ к его значению безопасным способом возможен, если мой базовый класс Property предоставляет доступ к его члену ValueType. ✓
  • Использование dynamic_cast или подобных конструкций не требуется ✓

Любые комментарии по этому подходу по-прежнему приветствуются.

person sigy    schedule 05.03.2014