c – 如何在不破坏Decorator模式的情况下减少胖界面?
在我的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; }; // 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,这样所有三种类型的接口都可以完全装饰,但我真的不愿意 – 这似乎是很多冗余/开销,特别是如果我有已经有多个不同的适配器类,我不想将它们的数字进一步倍增三倍. 是否有一些众所周知的聪明/优雅的方法来解决这个问题,这样我就可以吃蛋糕了? 解决方法
我不知道这是否是解决这个问题的最聪明/最优雅的方法,但经过一些反思,这就是我想出的:
1)对两个扩展接口使用“虚拟继承”: class ISeekableDataIO : public virtual IDataIO {...} class IPacketDataIO : public virtual IDataIO {...} 2)创建一个继承自这两个接口的DecoratorDataIO类,并在可能的情况下将所有方法调用传递给子IDataIO对象上的相应调用: class DecoratorDataIO : public IPacketDataIO,public ISeekableDataIO { public: DecoratorDataIO(const IDataIO * childIO) : _childIO(childIO),_seekableChildIO(dynamic_cast<ISeekableDataIO *>(childIO)),_packetChildIO(dynamic_cast<IPacketDataIO *>(childIO)) { // empty } virtual ~DecoratorDataIO() {delete _childIO;} // IDataIO interface implementation virtual ssize_t Read(void * buffer,size_t size) {return _childIO() ? _childIO()->Read(buffer,size) : -1;} virtual ssize_t Write(const void * buffer,size_t size) {return _childIO() ? _childIO()->Write(buffer,size) : -1;} // ISeekableDataIO interface implementation virtual result_t Seek(ssize_t offset,int whence) {return _seekableChildIO ? _seekableChildIO->Seek(offset,whence) : B_ERROR;} virtual ssize_t GetCurrentSeekPosition() const {return _seekableChildIO ? _seekableChildIO->GetCurrentSeekPosition() : -1;} virtual ssize_t GetStreamLength() const {return _seekableChildIO ? _seekableChildIO->GetStreamLength() : -1;} // IPacketDataIO interface implementation virtual const IPAddressAndPort & GetSourceOfLastReadPacket() const {return _packetChildIO ? _packetChildIO->GetSourceOfLastReadPacket() : GetDefaultObjectForType<IPAddressAndPort>();} virtual const IPAddressAndPort & GetPacketSendDestination() const {return _packetChildIO ? _packetChildIO->GetPacketSendDestination() : GetDefaultObjectForType<IPAddressAndPort>();} private: IDataIO * _childIO; ISeekableDataIO * _seekableChildIO; IPacketDataIO * _packetChildIO; }; 3)现在我的装饰器类可以只是子类化DecoratorDataIO并覆盖它们选择的任何方法(根据需要调用方法的超类实现): class XorDataIO : public DecoratorDataIO { public: XorDataIO(IDataIO * child) : DecoratorDataIO(child) {/* empty */} virtual ssize_t Read(void * buffer,size_t size) { ssize_t ret = DecoratorDataIO::Read(buffer,size_t size) { XorData(buffer,but you get the idea return DecoratorDataIO::Write(buffer,size); } }; 这种方法实现了我的目标,如果有一些丑陋(即dynamic_cast<>),至少它包含在DecoratorDataIO类中,并且不会暴露给所有装饰器子类. (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |