設計兩次 (Design It Twice)

★★★★★Draft

FromA Philosophy of Software Design

📚 From the Books

設計兩次 (Design It Twice)Complexity

Ousterhout 的原則:軟體設計很難,第一個冒出來的點子幾乎不可能是最佳設計。所以對每個重要的設計決策,都該考慮多個彼此差異夠大的選項,再從中挑選——或結合它們的優點。這就是「設計兩次」。它不只讓這次的設計更好,還會持續磨練你的設計技藝。

🧠 為什麼要多設計一次

NOTE

介面與實作的評估重點不同。設計介面時,最重要的考量是「對上層程式碼是否易用」;設計實作時,最看重的則是簡潔與效能。

⚖️ 同一個介面的兩種設計

以 GUI 編輯器的文字類別為例,設計介面時至少該擺出差異夠大的幾種風格來比:

方案 A — 行導向:以整行為單位插入、修改、刪除
// 介面以「行」為單位
class TextBuffer {
  insertLine(index, text) {}
  deleteLine(index) {}
  replaceLine(index, text) {}
}

// 上層若要處理「跨行」或「部分行」的操作,得自己拆行、合行
buffer.replaceLine(3, buffer.getLine(3).slice(0, 5) + "X");

行導向會把跨行 / 部分行的編排責任推回給上層。

方案 B — 字元導向:以個別字元插入、刪除
// 介面以「單一字元」為單位
class TextBuffer {
  insertChar(pos, ch) {}
  deleteChar(pos) {}
}

// 多字元操作只能靠迴圈;每個字元一次呼叫,可能顯著較慢
for (const ch of "hello") buffer.insertChar(pos++, ch);

字元導向讓上層得用迴圈處理,且每個字元都呼叫一次文字模組,實作可能明顯較慢。

行與字元兩個方案都逼上層做額外的文字操作——這正是紅旗。它提示介面應該與上層真正的操作對齊,而這個推理會把你導向第三種、也是可能更好的設計:範圍導向,直接對任意範圍的字元(可跨行)操作。最佳設計常常不是三者之一,而是結合多個方案優點的新設計。

TIP

比較方案時可問自己幾個問題:哪個介面更簡單?哪個更通用?哪個能讓實作更高效?這些提問會逼出方案之間真正的取捨。

CAUTION

「設計兩次」對非常聰明的人特別難接受。他們成長過程中,第一個直覺往往就足以拿好成績,於是養成「一次就做對」的習慣。但職涯推進到更困難的問題時,第一個點子終究會不再夠好——大型軟體系統就是這種問題,沒有人能一次就做對。堅持實作第一個點子,反而讓他們的真實潛力被低估。這不是不夠聰明,而是問題真的很難。

🔑 Takeaways

✍️ My Notes

No notes yet — jot your takeaways or Q&A here.

📖 Further Reading