一個只支援 HTML 報表的工具,卻預先蓋了
AbstractReport → DefaultReport → AnnotatedReport → HtmlReport四層階層,其中 annotation 功能根本不在 roadmap 上。程式看起來「很有彈性」,但沒人用得到那些彈性。這是過度設計——怎麼辨識、怎麼收?
🎯 Scenario
// 需求只有一種報表格式,卻預先為『想像中的未來』鋪了一整條 list-like 階層
abstract class AbstractReport
open class DefaultReport : AbstractReport()
open class AnnotatedReport : DefaultReport() // annotation 不在計畫上,純想像
class HtmlReport : AnnotatedReport() // 每層只有一個子型別
壞味道:不必要的複雜性(Needless Complexity)——設計中包含目前無用的結構。書中對應兩個具體味道:Unnecessary Abstraction(為便利或「為建而建」引入瑣碎抽象,例如替一個整數旗標硬開一個類別)與 Speculative Hierarchy(為想像中的需求預留型別,違反 YAGNI)。
🧠 Intuition
TIP
先別看下面。對每一層抽象問一句:現在有真實需求要它嗎? 如果理由是「萬一以後要呢」「這樣比較專業」「先留著彈性」——而不是眼前的故事——那它多半是投機性的結構。
⚖️ Reference Design
辨識訊號 — 這些理由都是紅旗
- 未來防備(future-proofing):「萬一以後需要呢?」——預留的擴充點多半猜錯形狀。
- 展現專業:加泛化型別來「秀技術」,實際需求不存在。
- list-like 階層:每層只有一個子型別、沒有分歧——階層在假裝有變化。
- 為瑣碎資料建類別 / 常數介面:
enum已能表達卻硬開一個類別(Unnecessary Abstraction)。
解法:collapse 掉推測性結構,回到最簡方案
// 移除推測性的中介型別(collapse hierarchy),需求出現時再長出來
interface Report { fun render(): String }
class HtmlReport : Report { override fun render() = "<html>…</html>" }
// 真的要支援 PDF/RTF 時,那時的需求會告訴你正確的抽象長相🔑 Takeaways
- 過度設計 = 不必要的複雜性:為想像中的需求預留結構,付出理解與維護成本卻換不到價值。
- 兩個典型味道:Unnecessary Abstraction(為建而建)、Speculative Hierarchy(為想像的未來預留型別)。
- 解法是 YAGNI + collapse 推測結構;等真實變更出現再抽象(呼應 OCP 的上當一次)。
No notes yet — jot your takeaways or Q&A here.