加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

c – 如何在不破坏Decorator模式的情况下减少胖界面?

发布时间:2020-12-16 06:53:42 所属栏目:百科 来源:网络整理
导读:在我的C库代码中,我使用抽象基类作为所有不同类型的I / O对象的接口.它目前看起来像这样: // All-purpose interface for any kind of object that can do I/Oclass IDataIO{public: // basic I/O calls virtual ssize_t Read(void * buffer,size_t size) =
在我的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类中,并且不会暴露给所有装饰器子类.

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读