我有一个数据结构,我想在不同的情况下以不同的方式访问/修改。我想出了这个:
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
函数中,这就是我在开始时所做的事情,但是对原始数据结构的操作变成了一个丑陋的索引操作。这就是为什么我想将它包装在一个对象中,这将隐藏所有这些。
答案 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的角度来看,根本不需要将基类设置为虚拟。
如果一个类与另一个类有“是一个”关系,它应该从这个类继承。 (例如,一条白鲨“是一条”鲨鱼,“它是一种”鱼,但你很可能只有一个只有普通鲨鱼或鱼类的系统。)
(与“有”关系相反,其他类应该是成员。例如,汽车“有”挡风玻璃。)