最好的模組:強大的功能藏在簡單的介面之後。 作者把這種模組稱為「深(deep)」。

視覺化:模組的「深度」#

把每個模組想像成一個矩形:

Figure 4.1: 深模組與淺模組——好的模組讓大量功能透過簡單介面被存取;淺模組介面複雜卻沒藏多少複雜性

  • 矩形面積 ∝ 模組實作的功能量
  • 矩形上邊長度 ∝ 介面的複雜度
  • 好模組是「深」的:大量功能藏在短短的上邊(簡單介面)後面

從成本與效益看深度#

  • 效益:模組提供的功能
  • 成本:模組對系統其他部分施加的複雜性 = 介面
  • 介面越小、越簡單,引入的複雜性越少

介面是好東西,但更多 / 更大的介面不一定更好

最好的模組是「效益最大、成本最小」。

範例:Unix I/O#

Unix(與 Linux)對檔案 I/O 的介面是深介面的經典:只有五個基本系統呼叫,簽章極其簡單。

int open(const char* path, int flags, mode_t permissions);
ssize_t read(int fd, void* buffer, size_t count);
ssize_t write(int fd, const void* buffer, size_t count);
off_t lseek(int fd, off_t offset, int referencePosition);
int close(int fd);
  • open 接受階層式檔名,回傳整數 file descriptor
  • read / write 在使用者緩衝區與檔案之間搬資料
  • lseek 用於隨機存取(預設是循序存取)
  • close 結束存取

現代實作需要數十萬行程式碼,要處理:

  • 檔案在磁碟上如何表示以支援高效存取
  • 目錄如何儲存?階層式路徑名如何解析?
  • 權限如何強制執行?
  • 中斷處理常式與背景程式如何分工、如何安全溝通?
  • 多檔案併發存取的排程策略?
  • 最近存取的資料如何快取?
  • 如何整合磁碟、快閃記憶體等不同二級儲存裝置?

所有這些問題對呼叫端完全不可見

Unix I/O 的實作多年來歷經劇烈演化,但這五個基本系統呼叫始終沒變

範例:垃圾回收(Garbage Collection)#

Go、Java 等語言的垃圾回收器是另一個深模組:

  • 介面是零——它在背景無聲運作回收未使用記憶體
  • 加入 GC 反而縮小了系統整體介面(移除了「釋放物件」的介面)
  • 實作非常複雜,但對語言使用者完全隱藏

深模組的價值#

Unix I/O 與 GC 都是強大的抽象範例:

  • 容易使用(介面簡單)
  • 隱藏巨大實作複雜性