包装数据结构

时间:2009-03-05 13:19:33

标签: c++ inheritance data-structures oop

我有一个数据结构,我想在不同的情况下以不同的方式访问/修改。我想出了这个:

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虽然应该是抽象的,但它并不是抽象的。我当然可以在构造函数中添加一些纯虚拟虚函数或断言(0),但这似乎太过于破解了解决方案。或者我应该完全摆脱继承,因为它只是真正用于代码重用?这个解决方案还有其他问题吗?

或者我完全走错了轨道?

编辑@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 个答案:

答案 0 :(得分:2)

考虑您希望数据基类的用途。如果您要保存数据或将其显示在屏幕上,您可能需要一个具有ToString和ToXML等功能的基类。

让代码发展。写出您需要的不同DataStructure类。然后找到共性并将其移动到基类中。

答案 1 :(得分:1)

好吧,对我来说似乎没问题。我建议为WrapperBase编写一个更彻底的界面(使用虚拟方法),如果可能的话, 。我不是要求你添加不必要的函数,而是建议让'Wrapper'界面更明确。与Thomi所建议的相反,我的建议涉及到预先修改界面 - 正如我已经说过的那样,这可能是不可能的。

答案 2 :(得分:1)

只是一个快速注释,你不能在Base构造函数中添加一个assert(0),因为无论你有多少继承,构造函数都会一直运行。

答案 3 :(得分:1)

我认为你在基类中缺少任何虚方法这一事实表明继承是浪费时间。所有WrapperOne和WrapperTwo共享的是它们使用相同的数据结构。如果你试图避免两次编写相同的业务逻辑来与DataStructure交互,那么考虑将DataStructure包装在一个类中以实现该业务逻辑,并让WrapperOne和WrapperTwo都使用(不是继承)该业务逻辑类。

答案 4 :(得分:1)

您可以将WrapperBase的默认和复制构造函数设置为“protected”。它不需要添加任何非功能性方法,并确保继承链之外的任何类都不能调用WrapperBase的构造函数。 或者只是废弃继承。

答案 5 :(得分:1)

让你的包装成为数据 创建将返回数据或不同包装器的工厂。 这就是我的意思。

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() );
}

答案 6 :(得分:0)

我认为我们需要更多地了解您需要以不同方式访问/修改它的原因,以及为什么您认为需要不同的类来执行此操作,以及您希望从中获得什么

答案 7 :(得分:0)

我认为这个解决方案没有错。

您可能会发现,在最后的轨道中,您最终会在WrapperBase类中添加一些常用代码,当这样做时,您会很高兴您有一个共同的基类。

您可能还会发现WrapperOne和WrapperTwo支持类似的界面。如果是这种情况,您可能希望使用纯虚方法在WrapperBase中定义该接口 - 这样您就可以使用指针到WrapperBase作为您实际使用的Wrapper的替身。

...希望有道理!

答案 8 :(得分:0)

我使用了一个teplated包装器,这样你就可以得到你需要的东西而且不需要为虚拟调用/接口支付任何费用。

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

然后,您可以专门化Wrappers而不需要基类。

答案 9 :(得分:0)

一般来说,虽然我同意基类可能应该考虑两次并包含虚方法,但从OO的角度来看,根本不需要将基类设置为虚拟。
如果一个类与另一个类有“是一个”关系,它应该从这个类继承。 (例如,一条白鲨“是一条”鲨鱼,“它是一种”鱼,但你很可能只有一个只有普通鲨鱼或鱼类的系统。)
(与“有”关系相反,其他类应该是成员。例如,汽车“有”挡风玻璃。)