在我的C++库代码中,我使用一个抽象基类作为所有不同类型的支持I/O的对象的接口。它目前看起来是这样的:
// All-purpose interface for any kind of object that can do I/O
class IDataIO
{
public:
// basic I/O calls
virtual ssize_t Read(void * buffer, size_t size) = 0;
virtual ssize_t Write(const void * buffer, size_t size) = 0;
// Seeking calls (implemented to return error codes
// for I/O objects that can't actually seek)
virtual result_t Seek(ssize_t offset, int whence) = 0;
virtual ssize_t GetCurrentSeekPosition() const = 0;
virtual ssize_t GetStreamLength() const = 0;
// Packet-specific calls (implemented to do nothing
// for I/O objects that aren't packet-oriented)
virtual const IPAddressAndPort & GetSourceOfLastReadPacket() const = 0;
virtual result_t SetPacketSendDestination(const IPAddressAndPort & iap) = 0;
};这非常有效--我有各种具体的子类,用于TCP、UDP、文件、内存缓冲区、SSL、RS232、stdin/stdout等等,而且我能够编写与I/O无关的例程,这些例程可以与它们中的任何一个一起使用。
我还有各种decorator类,它们获得现有IDataIO对象的所有权,并充当该对象的行为修改前端。这些装饰器类很有用,因为可以使用单个装饰器类来修改/增强任何类型的IDataIO对象的行为。下面是一个简单的(玩具)示例:
/** Example decorator class: This object wraps any given
* child IDataIO object, such that all data going out is
* obfuscated by applying an XOR transformation to the bytes,
* and any data coming in is de-obfuscated the same way.
*/
class XorDataIO : public IDataIO
{
public:
XorDataIO(IDataIO * child) : _child(child) {/* empty */}
virtual ~XorDataIO() {delete _child;}
virtual ssize_t Read(void * buffer, size_t size)
{
ssize_t ret = _child->Read(buffer, size);
if (ret > 0) XorData(buffer, ret);
return ret;
}
virtual ssize_t Write(const void * buffer, size_t size)
{
XorData(buffer, size); // const-violation here, but you get the idea
return _child->Write(buffer, size);
}
virtual result_t Seek(ssize_t offset, int whence) {return _child->Seek(offset, whence);}
virtual ssize_t GetCurrentSeekPosition() const {return _child->GetCurrentSeekPosition();}
virtual ssize_t GetStreamLength() const {return _child->GetStreamLength();}
virtual const IPAddressAndPort & GetSourceOfLastReadPacket() const {return _child->GetSourceOfLastReadPacket();}
virtual result_t SetPacketSendDestination(const IPAddressAndPort & iap) {return _child->SetPacketSendDestination(iap);}
private:
IDataIO * _child;
};这一切都很好,但困扰我的是,我的IDataIO类看起来像一个fat interface示例--例如,UDPSocketDataIO类永远不能实现Seek()、GetCurrentSeekPosition()和GetStreamLength()方法,而FileDataIO类永远不能实现GetSourceOfLastReadPacket()和SetPacketSendDestination()方法。因此,这两个类都被迫将这些方法作为存根来实现,这些存根什么也不做,并返回一个错误代码--这是可行的,但是很难看。
为了解决这个问题,我想将IDataIO接口拆分成单独的块,如下所示:
// The bare-minimum interface for any object that we can
// read bytes from, or write bytes to (e.g. TCP or RS232)
class IDataIO
{
public:
virtual ssize_t Read(void * buffer, size_t size) = 0;
virtual ssize_t Write(const void * buffer, size_t size) = 0;
};
// A slightly extended interface for objects (e.g. files
// or memory-buffers) that also allows us to seek to a
// specified offset within the data-stream.
class ISeekableDataIO : public IDataIO
{
public:
virtual result_t Seek(ssize_t offset, int whence) = 0;
virtual ssize_t GetCurrentSeekPosition() const = 0;
virtual ssize_t GetStreamLength() const = 0;
};
// A slightly extended interface for packet-oriented
// objects (e.g. UDP sockets)
class IPacketDataIO : public IDataIO
{
public:
virtual const IPAddressAndPort & GetSourceOfLastReadPacket() const = 0;
virtual result_t SetPacketSendDestination(const IPAddressAndPort & iap) = 0;
};……因此,现在我可以从IPacketDataIO子接口派生UDPSocketDataIO子类,从ISeekableDataIO接口派生FileDataIO子类,而TCPSocketDataIO仍然可以直接从IDataIO派生子类,依此类推。这样,每种类型的I/O对象只向它实际支持的功能提供接口,而没有人必须实现与它们所做的事情无关的方法的无操作/存根版本。
到目前为止,一切顺利,但此时出现的问题是装饰器类--在这个场景中,我的XorDataIO子类应该从什么接口继承?我想我可以编写一个XorDataIO、一个XorSeekableDataIO和一个XorPacketDataIO,这样所有三种类型的接口都可以完全修饰,但我真的不希望这样做--这似乎有很多冗余/开销,特别是如果我已经有多个不同的适配器类,并且我不想将它们的数量再乘以三倍。
有没有一些众所周知的聪明/优雅的方法来解决这个问题,这样我就可以既吃蛋糕又吃蛋糕了?
https://stackoverflow.com/questions/44555394
复制相似问题