C++ 包装类的设计

Kac*_*kao 3 c++ design-patterns wrapper

我必须使用一个界面非常笨拙的旧类。由于我无法更改它并且依赖它,因此我想构建一个包装器,提供一个干净的界面。假设我有一堂课ClumsyClass。基本上,我有三种方法:

1. 参考会员

    Class Wrapper {
      public:
        Wrapper (ClumsyClass& clumsyClass)
          : m_clumsyClass(clumsyClass)
        { }

        int getSmth() {
          return m_clumsyClass.getSmth();
        }

        private:
          ClumsyClass& m_clumsyClass;
}
Run Code Online (Sandbox Code Playgroud)

2. 指针成员

    Class Wrapper {
      public:
        Wrapper (ClumsyClass* clumsyClass)
          : m_clumsyClass(clumsyClass)
        { }

        int getSmth() {
          return m_clumsyClass->getSmth();
        }

        private:
          ClumsyClass* m_clumsyClass;
}
Run Code Online (Sandbox Code Playgroud)

3、继承

    Class Wrapper : public ClumsyClass {
    ...
}
Run Code Online (Sandbox Code Playgroud)

哪种方法是实现包装器的“最干净”方法?我更喜欢第三个,但是当我已经有一个 ClumsyClass 对象然后创建一个 Wrapper 对象(复制构造函数)时,将需要更多内存(因为在我的情况下需要原始类的实例)。

Chr*_*rew 5

我会避免使用 3,因为它无法封装ClumsyClass. 用户可以Wrapper有意或无意地直接访问“笨拙”的界面,而ClumsyClass这正是您试图避免的。优先选择组合而不是继承。

1. 和 2. 之间的差异很小。使用参考构件会降低包装器的灵活性。该类不可分配,您无法重新设置引用并将其替换为不同的实例,ClumsyClass并且成员不能为空。这些可能是好事也可能是坏事,具体取决于您的要求。

但正如评论中提到的,默认选择可能应该是作为 的ClumsyClass按值成员Wrapper

class Wrapper {
  public:
    // possible constructors
    //Wrapper(const ClumsyClass& cc) : m_clumsyClass(cc) {} // copy 
    //Wrapper(ClumsyClass&& cc) : m_clumsyClass(std::move(cc)) {}  // move 
    int getSmth() { return m_clumsyClass.getSmth(); }

  private:
    ClumsyClass m_clumsyClass;
};
Run Code Online (Sandbox Code Playgroud)

在您的特定用例中,有多种原因可能导致这种情况不可能或不理想,然后您可以回退到选项 1 或 2。该决定主要取决于所有权。是否应该Wrapper“拥有”ClumsyClass或 的实例是否ClumsyClass具有超出 的生命周期Wrapper

使用直接成员的一个潜在缺点是您无法再隐藏ClumsyClass前向声明后面的实现,因此您会失去一些ClumsyClass. 值得注意的是,解决这个问题的一种方法是提取一个Wrapper继承自的抽象基类“接口”。就像是:

class IWrapper {
  public:
    virtual ~IWrapper() {}
    virtual int getSmth() = 0;
};
Run Code Online (Sandbox Code Playgroud)

这可能会提供额外的好处,例如可测试性。