問題描述
Java 有語法 interface 明確定義 class 之間的接口, 但是 C++ 沒有, 只能透過「習以為常」的慣例表示, 也就是:
- class I 宣告一組 public pure virtual function, 表示 I 是一個 "interface"
- class A 希望實作 I, 於是透過繼承的方式實作 I
- 需要用 I* 的 class, 取得實作 I 的物件 (也就是 A 的物件), 存成 I*
當 class A 需要實作多組介面, 或是自己也有一些 public method 供別人使用時, 看 class A 的宣告會不易找出那些是 A 的 public API, 比方說以下的例子:
class I { public: virtual void foo() = 0; virtual void bar() = 0; }; class A : public I { public: void handle(); virtual void process(); // I's methods. virtual void foo(); virtual void bar(); };
一眼看去有四個 method, 實際上可能只有 handle() 和 process() 才是使用 A 的人需要關心的。當 A 同時實作 (繼承) 多組介面就不易閱讀了。
解法
若宣告實作介面的 method 為 private, 像下面這樣:
class A : public I { public: void handle(); virtual void process(); private: // I's methods. virtual void foo(); virtual void bar(); };
有幾點好處:
- 降低這些 method 可存取的範圍, 避免被誤用
- 縮小閱讀程式的範圍
- 間接暗示它們是作為 I 的介面使用, 若 A 自己的程式碼都沒用到它們的話, 會更明確地表明此點。特別適合用於 callback 的介面。
宣告 private virtual 的基本精神和宣告 member field 為 private 差不多: 不需要用到的, 就不要曝露出去, 易於日後維護。
相關閱讀
本篇討論的使用情境很窄, 專注於一點: 需要在 C++ 的世界裡表示出「Java interface」的語意時, 可使用「在介面 class 宣告 public pure virtual method + 在實作 class 宣告 private virtual method」。
Virtuality 提出關於 virtual 的四項要點:
- Prefer to make interfaces nonvirtual, using Template Method.
- Prefer to make virtual functions private.
- Only if derived classes need to invoke the base implementation of a virtual function, make the virtual function protected.
- A base class destructor should be either public and virtual, or protected and nonvirtual.
涵蓋關於 private virtual 的更多應用情境, 滿不錯的參考資料。
我覺得最主要的是interface跟customize method分開來, interface不需要用virtual, 而是把需要customize的部分放在private virtual.
回覆刪除一方面可以針對customize的部分做一些pre-process or post-process, 因為是放在interface method 而不是在virtual method. 也可以規範customized的行為 (像是一定要夾在某個method之後)
可讀性我覺得差不多
> 我覺得最主要的是interface跟customize method分開來, interface不需要用 virtual, 而是把需要customize的部分放在private virtual.
刪除>
> 一方面可以針對customize的部分做一些pre-process or post-process, 因為是放在interface method 而不是在virtual method. 也可以規範customized的行為 (像是一定要夾在某個method之後)
適合用 Template method 的情境, 這樣做確實很棒, 像是 XML parser 的 callback, 或是某些操作的 pre-commit, post-commit
> 可讀性我覺得差不多
我原本的出發點是為啥要用 private virtual, 查一查後想通背後的意思, 再回頭從需求出發, 描述這樣作的好處
method 一大票時這樣滿方便的, 基本精神和 member field 預設都 private 差不多, 不該用到的, 就不要曝露出來
來稍微改寫一下文章, 補充這點