接口隔离

2026/04/10

背景

起因是在写 CS144 的 lab0 时,看了一下 ByteStream 的声明(删去了一些无关的函数):

class Reader;
class Writer;

class ByteStream
{
public:
  explicit ByteStream( uint64_t capacity );

  Reader& reader();
  const Reader& reader() const;
  Writer& writer();
  const Writer& writer() const;

protected:
  // Please add any additional state to the ByteStream here, and not to the Writer and Reader interfaces.
  uint64_t capacity_;
  bool error_ {};
};

class Writer : public ByteStream { ... };

class Reader : public ByteStream { ... };

从继承关系看,WriterReaderByteStream 的子类,那为什么 ByteStream 还需要有获取 WriterReader 的方法呢?

这就引出了接口隔离原则。

接口隔离

WriterReader 并非独立存在的类,而是 ByteStream 的"视图" (View)。

先看一下 reader() 的实现:

Reader& ByteStream::reader()
{
  static_assert( sizeof( Reader ) == sizeof( ByteStream ),
                 "Please add member variables to the ByteStream base, not the ByteStream Reader." );
    //static_assert 确保 Reader 只是"视图"而非独立类——它不能有额外的成员变量

  return static_cast<Reader&>( *this ); // NOLINT(*-downcast)
}

返回的 Reader 对象内部实际引用的是同一个 ByteStream。也就是说,WriterReader 都是 ByteStream 的一部分——两者共享来自父类的函数,同时各自拥有独有的方法。

设计优势

这样设计的理由如下:

1. 共享状态

Reader 和 Writer 操作的是同一个缓冲区,只是提供不同的访问接口。

2. 接口隔离

每个接口只暴露相关的方法,保证了职责单一。

3. 避免菱形继承

如果使用继承来实现类似功能,会形成菱形继承结构:

graph TD
    A[ByteStream] --> B[Writer]
    A --> C[Reader]
    B --> D[MyStream]
    C --> D

这会导致问题:

  class ByteStream {
  public:
      uint64_t capacity_ = 100;
      bool error_ = false;
  };

  class Writer : public ByteStream {
      // 继承 ByteStream,拥有自己的一份 capacity_ 和 error_
  };

  class Reader : public ByteStream {
      // 继承 ByteStream,拥有自己的一份 capacity_ 和 error_
  };

  // 危险!如果同时继承两者
  class MyStream : public Writer, public Reader {
      // 现在有两份 capacity_ 和 error_!
  };

菱形继承会导致 MyStream 中存在两份 ByteStream 的成员变量,破坏了状态的唯一性。