如何在不破坏装饰器模式的情况下瘦身胖界面?
How to slim down a Fat Interface without breaking the Decorator pattern?
在我的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无关的例程,可以与其中任何一个结合使用。
我还有各种装饰器类,它们获取现有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
类看起来像一个胖接口的例子——例如,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
,这样所有三种类型的接口都可以完全装饰,但我真的宁愿不 - 这似乎是很多冗余/开销,特别是如果我有多个不同的适配器类,我不想将它们的数量进一步乘以三倍。
有没有一些众所周知的聪明/优雅的方法来解决这个问题,这样我也可以吃我的蛋糕?
我不知道这是否是解决这个问题的最聪明/最优雅的方法,但经过更多的思考,这就是我想出的:
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);
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 DecoratorDataIO::Write(buffer, size);
}
};
这种方法实现了我的目标,如果有一些丑陋(即dynamic_cast<>),至少它包含在DecoratorDataIO
类中,而不是暴露给所有装饰器子类。
- 在没有太多条件句的情况下,我如何避免被零除
- 为什么在没有显式默认构造函数的情况下,将另一个结构封装在联合中作为成员的结构不能编译
- 在未初始化映射的情况下,将值插入到映射的映射中
- 是默认情况下分配给char数组常量的值
- 在这种情况下我应该使用哪种设计模式
- 如何在不破坏装饰器模式的情况下瘦身胖界面?
- 在我的情况下,多重继承是一种好的设计模式吗?
- 在没有指针的情况下以C 实施策略模式
- 在某些情况下,通配符模式匹配失败
- 如何在不必绑定到特定类的情况下实现观察者模式
- 如何在不参考文档的情况下获得 g++ 编译器的 c++ 的默认模式
- 如何在过剩的情况下获取当前窗口模式
- 用于在不使用 RTTI 的情况下缓存不同派生类型的设计模式
- 如何在不使用非构造函数的方法的情况下应用singleton设计模式来返回类对象
- 在什么情况下,单例设计模式可能会生成同一单例类的多个实例
- 在什么情况下我们应该采用国家模式
- 如何在不使其过度嵌套的情况下使此模式根据需要重复多次
- 在这种情况下,哪种设计模式有利于实现运行 tiime 多态性
- 在没有正则表达式的情况下拾取特定的字符串模式
- 我可以在不使用new()的情况下实现工厂模式构造吗?