穿透型方法(pass-through method):除了呼叫另一個方法外幾乎什麼都不做,且被呼叫方法的簽章與自己相似或相同

真實案例#

某學生 GUI 編輯器專案的 TextDocument 類別:

public class TextDocument ... {
    private TextArea textArea;
    private TextDocumentListener listener;
    ...
    public Character getLastTypedCharacter() {
        return textArea.getLastTypedCharacter();
    }
    public int getCursorOffset() {
        return textArea.getCursorOffset();
    }
    public void insertString(String textToInsert, int offset) {
        textArea.insertString(textToInsert, offset);
    }
    public void willInsertString(String stringToInsert, int offset) {
        if (listener != null) {
            listener.willInsertString(this, stringToInsert, offset);
        }
    }
    ...
}

15 個 public 方法中有 13 個是穿透型方法。

紅旗:穿透型方法(Red Flag: Pass-Through Method)#

穿透型方法只把參數轉送給另一個方法(通常 API 跟自己一樣)。

這通常代表類別之間的職責分割不乾淨

為什麼有害#

  • 使類別變淺:增加介面複雜度,卻沒有增加系統整體功能
    • 上述四個方法,只有 willInsertString 有實質工作(檢查 listener 是否為 null),且還很瑣碎
  • 製造類別間依賴:底層方法簽章一改,穿透型方法就得跟著改

根本原因#

穿透型方法往往代表類別之間的職責劃分混亂

  • TextDocument 暴露了 insertString,但插入文字的真正邏輯在 TextArea
  • 一個功能的介面與實作應該位於同一個類別

當你看到一個類別到另一個類別的穿透型方法,問自己:

「這兩個類別到底各自負責哪些特性與抽象?」

通常會發現職責有重疊。

解決方式#

方式說明
直接暴露下層移除上層對該功能的責任,讓上層的呼叫者直接呼叫下層類別
重新分配在上下兩個類別之間搬移功能,讓它們不再互相呼叫
合併若兩個類別職責糾結到拆不開,乾脆合併成一個

Figure 7.1: 穿透型方法——(a) C1 全是穿透;(b) 直接暴露 C2;(c) 在 C1、C2 之間重分配;(d) 合併兩類別

範例中的學生最後合併了三個類別(TextDocumentTextAreaTextDocumentListener)成兩個,職責也分得更清楚。