【问题标题】:Wrapping a data structure包装数据结构
【发布时间】:2009-03-05 13:19:33
【问题描述】:

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

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) ,但这似乎是一个太老套的解决方案。或者我应该完全摆脱继承,因为它只是为了代码重用而完成的?此解决方案还有其他问题吗?

还是我完全走错了路?

编辑@保罗

我为什么要这样做?好吧,我得到了几个 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 函数中,这就是我一开始正在做的事情,但是对原始数据结构的操作变成了丑陋的索引操作混乱。这就是为什么我想将它包装在一个对象中,这将隐藏所有这些。

【问题讨论】:

  • 请给我们至少一种方法 // 以其他方式访问/修改数据结构

标签: c++ inheritance data-structures oop


【解决方案1】:

想想你希望你的数据基类做什么。如果您要保存数据或将其显示到屏幕上,您可能需要一个具有 ToString 和 ToXML 等函数的基类。

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

【讨论】:

    【解决方案2】:

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

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

    【讨论】:

    • 您从 DataStructure 派生,同时在包装器中保留指向 DataStructure 对象的指针。这样做有什么意义?
    • Derive - 能够传递包装器而不是 DataStructure。保持指针 - 能够实现获取和设置。在简单的情况下,它将是 dataStructure_->Get()。可能是 dataStructure_->Get() * 2;
    【解决方案3】:

    简单评论一下,你不能真正在 Base 构造函数中添加 assert(0),因为无论你有多少继承,构造函数都会一直运行。

    【讨论】:

      【解决方案4】:

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

      【讨论】:

        【解决方案5】:

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

        【讨论】:

          【解决方案6】:

          嗯,我觉得还可以。我建议为 WrapperBase 编写一个更彻底的接口(使用虚拟方法),如果可能的话。我不是要你添加不必要的功能,而是建议让“Wrapper”界面更加明确。与 Thomi 的建议相反,我的建议涉及预先确定要修改的接口——正如我已经说过的那样,这可能是不可能的。

          【讨论】:

            【解决方案7】:

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

            【讨论】:

              【解决方案8】:

              我认为这个解决方案没有任何问题。

              您很可能会发现,您最终会在 WrapperBase 类中添加一些通用代码,而当发生这种情况时,您会很高兴拥有一个通用基类。

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

              ...希望这是有道理的!

              【讨论】:

              • “您可能还会发现 WrapperOne 和 WrapperTwo 支持类似的接口”——但可能不是,因为这样做的原因似乎是访问和修改是以不同的方式完成的?
              【解决方案9】:

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

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

              然后您可以专门化包装器而不需要基类。

              【讨论】:

              • 模板化包装器不是只允许修改操作的类型而不是功能吗?
              • *数据类型——在这种情况下,术语数据结构更合适。
              • 您可以根据 teplate 类型专门化模板和方法。这是一个强大的概念。
              【解决方案10】:

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

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 2011-02-10
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2021-03-10
                相关资源
                最近更新 更多