提取類別 (Extract Class)

★★★★Draft

FromRefactoring

📚 From the Books

提取類別 (Extract Class)Refactoring Moves

類別初期都很守規矩,但隨時間責任越積越重,複雜到失去彈性。當你看見某群資料與方法該待在一起、或有一群資料總是一起變動、彼此依賴,就把它們拆出成一個新類別。

🧠 Why It's a Problem

一個類別承擔太多職責時,它變得難懂難改。拆解的訊號有二,都指向「這裡其實藏著另一個概念」:

TIP

Fowler 給的自問法:「如果我移除某段資料或方法,其他欄位或方法會不會變得不合理?」 會一起「失去意義」的東西,就該搬進同一個新類別。

⚖️ Refactoring

機械步驟:① 決定如何切分職責 → ② 建立新類別承載被切出的那組資料 → ③ 用 Move Function 把相關方法搬進新類別 → ④ 在原類別持有新類別的實例、透過它委託 → ⑤ 測試。

前 — Employee 兼管地址細節
class Employee {
  constructor(name, email, street, city, zipCode) {
    this.name = name;
    this.email = email;
    this.street = street;
    this.city = city;
    this.zipCode = zipCode;
  }

  printEmployeeDetails() {
    console.log(`Name: ${this.name}`);
    console.log(`Email: ${this.email}`);
    console.log(`Address: ${this.street}, ${this.city}, ${this.zipCode}`);
  }
}

street / city / zipCode 這群資料總是一起出現、一起變動——它們其實是一個「地址」概念,卻擠在 Employee 裡。

後 — 把地址提取成獨立類別
class Address {
  constructor(street, city, zipCode) {
    this.street = street;
    this.city = city;
    this.zipCode = zipCode;
  }

  printAddress() {
    console.log(`Address: ${this.street}, ${this.city}, ${this.zipCode}`);
  }
}

class Employee {
  constructor(name, email, address) {
    this.name = name;
    this.email = email;
    this.address = address;
  }

  printEmployeeDetails() {
    console.log(`Name: ${this.name}`);
    console.log(`Email: ${this.email}`);
    this.address.printAddress();
  }
}

Address 有了自己的資料與行為,Employee 透過組合持有它。地址格式要改,只動 Address

NOTE

這個手法常接在 Introduce Parameter Object 之後:先把成群的參數收成物件,等它長出行為,再正式提取成類別。搬移方法本身則靠 Move Function

🔑 Takeaways

✍️ My Notes

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

📖 Further Reading