代码之家  ›  专栏  ›  技术社区  ›  drby

包装数据结构

  •  2
  • drby  · 技术社区  · 16 年前

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

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

    我在这里看到的一个问题是,包装库不是抽象的,即使它应该是抽象的。当然,我可以添加一些纯虚拟虚函数 或构造函数中的断言(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 函数,这就是我在开始时所做的,但是在原始数据结构上的操作变成了索引操作的一团丑陋的混乱。这就是为什么我想把它包在一个物体里,它可以隐藏所有的东西。

    10 回复  |  直到 16 年前
        1
  •  1
  •   Mykola Golubyev    16 年前

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

    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() );
    }
    
        2
  •  2
  •   banno    16 年前

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

    让代码进化。写出您需要的不同的数据结构类。然后找到共性并将其转移到基类中。

        3
  •  1
  •   Mizipzor    16 年前

    只需一个简短的注释,您就不能在基本构造函数中真正添加断言(0),因为无论您有多少继承,构造函数都将始终运行。

        4
  •  1
  •   Pesto    16 年前

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

        5
  •  1
  •   user21714    16 年前

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

        6
  •  1
  •   batbrat    16 年前

    嗯,我觉得没问题。我建议为WrapperBase编写一个更全面的接口(使用虚拟方法)。 如果可能的话 . 我并不是要求您添加不必要的函数,而是建议将“包装器”接口更显式。与托米的建议相反,我的建议涉及到预先确定修改的接口——正如我已经说过的,这可能是不可能的。

        7
  •  0
  •   The Archetypal Paul    16 年前

    我认为我们需要更多地了解为什么您需要以不同的方式访问/修改它,以及为什么您看到不同的类需要这样做,以及您希望从中获得什么。

        8
  •  0
  •   Thomi    16 年前

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

    你很可能会发现,沿着这条轨迹,你最终会向WrapperBase类添加一些公共代码,当这个Haoppens出现时,你会很高兴你有一个公共的基类。

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

    …希望这有道理!

        9
  •  0
  •   Robert Gould    16 年前

    我将使用一个温度相关的包装器,这样您就可以得到您需要的东西,而不必为虚拟调用/接口支付任何费用。

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

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

        10
  •  0
  •   E Dominique    16 年前

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