類別初期都很守規矩,但隨時間責任越積越重,複雜到失去彈性。當你看見某群資料與方法該待在一起、或有一群資料總是一起變動、彼此依賴,就把它們拆出成一個新類別。
🧠 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
- Extract Class 把一個責任過重的類別,沿著「總是一起變動的資料」切開成新類別。
- 觸發訊號:某群資料與方法該待在一起、一群資料同時變動且互相依賴。
- 自問法:移除某段資料/方法後,其餘會不會變得不合理?
- 實作上用 Move Function 搬方法,再以組合持有新類別。
No notes yet — jot your takeaways or Q&A here.