Обтекание структуры данных - PullRequest
2 голосов
/ 05 марта 2009

У меня есть структура данных, к которой я хочу обращаться / изменять по-разному в разных ситуациях. Я придумал это:

class DataStructure
{
    public:
       int getType();

    private:
       // underlying data containers    
};

class WrapperBase
{
    public:
        void wrap(DataStructure *input)
            {dataStructure = input;}

    protected:
        DataStructure *dataStructure;
};

class WrapperOne : public WrapperBase
{
     public:
          // @Mykola: I know bytes 4-10 in an array of type one specify the date
          // so this method will format those bytes and return them
          Data getDate()
};

class WrapperTwo : public WrapperBase
{
     public:
          // @Mykola: There is mostly just a chunk of data in packet type two. So this
          // method will turn 4 bytes into an int at position index and return that
          int dataAt(int index);              
};

Одна проблема, которую я вижу здесь, заключается в том, что WrapperBase не является абстрактным, даже если так и должно быть. Конечно, я мог бы добавить в конструктор некоторую чисто виртуальную фиктивную функцию или assert (0), но это кажется слишком хакерским решением. Или я должен полностью избавиться от наследования, поскольку оно действительно сделано только для повторного использования кода? Любые другие проблемы с этим решением?

Или я совсем не на том пути?

Edit @ Paul

Почему я хочу это сделать? Ну, я получил несколько 1000 массивов сериализованных данных, которые я хочу добавить в набор данных. Первые несколько байтов каждого массива говорят мне, что это за данные, что определяет, как я их обработал. Итак, я делаю что-то вроде:

// some place in the program
dataSet.processData(dataStructure);

// in the data set class
DataSet::processData(DataStructure *dataStructure)
{
     if(dataStructure->getType() == TYPE_ONE)
     {
          WrapperOne wrapperOne;
          wrapperOne.wrap(dataStructure); 
          processDataTypeOne(wrapperOne); 
     }

     // repeat the above for other data types
}

Конечно, я мог бы поместить всю логику в функцию processDataTypeOne, и это было то, чем я занимался в начале, но работа с необработанной структурой данных превратилась в ужасный беспорядок операций с индексами. Вот почему я хотел бы обернуть его в объект, который скроет все это.

Ответы [ 10 ]

2 голосов
/ 05 марта 2009

Подумайте, что вы хотите, чтобы ваш базовый класс делал с вашими данными. Если вы собираетесь сохранить свои данные или отобразить их на экране, вам может потребоваться базовый класс с такими функциями, как ToString и ToXML.

Пусть код эволюционирует. Запишите различные классы DataStructure, которые вам нужны. Затем найдите общность и переместите ее в базовый класс.

1 голос
/ 05 марта 2009

Вы можете сделать конструктор по умолчанию и копировать конструкторы WrapperBase как «защищенные». Он не требует добавления каких-либо нефункциональных методов и гарантирует, что никакие классы вне цепочки наследования не смогут вызвать конструктор WrapperBase. Или просто собери все наследство.

1 голос
/ 05 марта 2009

Сделайте ваши обертки данными.
Создайте фабрику, которая будет возвращать либо данные, либо разные оболочки. Вот что я имею в виду.

class DataStructure
{
public:
    typedef int DataType;

    DataStructure( int id ):
        id_( id )
    {}

    DataStructure( const DataStructure& dataStructure );
    virtual ~DataStructure();

    virtual void Set( const DataType& data ) { data_ = data; } 
    virtual DataType Get() const { return data_; }

    int id() const { return id_; }
private:
    DataType data_;
    int id_;
};

class WrapperBase : public DataStructure
{ 
public:
    WrapperBase( DataStructure* dataStructure ):
        DataStructure( dataStructure->id() ),
        dataStructure_( dataStructure )
    {}

    virtual void Set( const DataType& data );
    virtual DataType Get() const;
protected:
    DataStructure* dataStructure_;
};

class WrapperOne : public WrapperBase
{ 
public:
    WrapperOne( DataStructure* dataStructure );
    virtual void Set( const DataType& data );
    virtual DataType Get() const;
};

class WrapperTwo : public WrapperBase
{ 
public:
    WrapperTwo( DataStructure* dataStructure );
    virtual void Set( const DataType& data );
    virtual DataType Get() const;
};

DataStructure* getWrapper( DataStructure* dataStructure )
{
    switch ( dataStructure->id() )
    {
        case 1: return new WrapperOne( dataStructure );
        case 2: return new WrapperTwo( dataStructure );
        default: return new DataStructure( *dataStructure );
    }
}

void processData(DataStructure *dataStructure)
{
    std::auto_ptr<DataStructure> wrapper( getWrapper( dataStructure ) );
    processDataImpl( wrapper.get() );
}
1 голос
/ 05 марта 2009

Я думаю, тот факт, что вам не хватает каких-либо виртуальных методов в базовом классе, является показателем того, что наследование является пустой тратой времени. Все общие ресурсы WrapperOne и WrapperTwo заключаются в том, что они используют одну и ту же структуру данных. Если вы пытаетесь избежать написания одной и той же бизнес-логики дважды для взаимодействия с DataStructure, то подумайте об обёртывании DataStructure в классе для реализации этой бизнес-логики и чтобы оба WrapperOne и WrapperTwo оба использовали (не наследовали) этот класс бизнес-логики.

1 голос
/ 05 марта 2009

Только один быстрый комментарий, вы не можете добавить assert (0) в конструктор Base, так как конструктор всегда будет выполняться независимо от того, сколько у вас наследования.

1 голос
/ 05 марта 2009

Ну, мне кажется, это нормально. Я бы предложил написать более полный интерфейс (с виртуальными методами) для WrapperBase, , если это возможно . Я не прошу вас добавлять ненужные функции, вместо этого я предлагаю сделать интерфейс Wrapper более явным. В отличие от того, что предложил Томи, мое предложение предполагает предварительное определение интерфейса для модификации - как я уже сказал, это может быть невозможно.

0 голосов
/ 05 марта 2009

В целом, хотя я согласен с тем, что базовый класс , вероятно, следует продумать дважды и содержать виртуальные методы, с точки зрения ОО вообще не нужно иметь базовый класс виртуальным. Если класс имеет отношение «есть» к другому классу, он должен наследовать от этого класса. (Например, белая акула - это «акула, которая» является «рыбой», но у вас вполне может быть система, полная только простых акул или рыб.)
(В отличие от отношения "имеет", где другой класс должен быть членом. Например, у автомобиля "есть" ветровое стекло.)

0 голосов
/ 05 марта 2009

Я бы использовал тепловую оболочку, чтобы вы получали то, что вам нужно, и ничего не платили за виртуальные звонки / интерфейс.

   template<T>
   class Wrapper
   { 
     Wrapper( const T& data )

После этого вы можете специализировать Wrappers без базового класса.

0 голосов
/ 05 марта 2009

Я не вижу ничего плохого в этом решении.

Вы можете обнаружить, что в конечном итоге вы добавляете некоторый общий код в класс WrapperBase, и когда это произойдет, вы будете рады, что у вас есть общий базовый класс.

Вы также можете обнаружить, что WrapperOne и WrapperTwo поддерживают аналогичный интерфейс. Если это так, вы можете определить этот интерфейс в WrapperBase, используя чисто виртуальные методы - таким образом вы можете использовать указатель на WrapperBase в качестве замены для любого Wrapper, который вы фактически используете.

... надеюсь, это имеет смысл!

0 голосов
/ 05 марта 2009

Я думаю, нам нужно больше узнать о том, почему вам нужно получить к нему доступ / изменить его по-разному, и почему вы видите, что для этого нужны разные классы, и что вы ожидаете получить от этого

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...