首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何在不破坏装饰者模式的情况下瘦身?

如何在不破坏装饰者模式的情况下瘦身?
EN

Stack Overflow用户
提问于 2017-06-15 05:59:28
回答 0查看 142关注 0票数 4

在我的C++库代码中,我使用一个抽象基类作为所有不同类型的支持I/O的对象的接口。它目前看起来是这样的:

代码语言:javascript
复制
// 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对象的行为。下面是一个简单的(玩具)示例:

代码语言:javascript
复制
/** 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接口拆分成单独的块,如下所示:

代码语言:javascript
复制
// 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,这样所有三种类型的接口都可以完全修饰,但我真的不希望这样做--这似乎有很多冗余/开销,特别是如果我已经有多个不同的适配器类,并且我不想将它们的数量再乘以三倍。

有没有一些众所周知的聪明/优雅的方法来解决这个问题,这样我就可以既吃蛋糕又吃蛋糕了?

EN

回答

页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/44555394

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档