問題:需透過用戶輸入建立物件,建構子該怎麼寫?
// 不推薦
public class User {
String name;
public User() {
Scanner sc = new Scanner(System.in);
System.out.print("輸入姓名: ");
this.name = sc.nextLine(); // 測試困難、耦合標準輸入、異常處理混雜
}
}
// 推薦
public class User {
private final String name;
public User(String name) { this.name = name; } // 純初始化
public String getName() { return name; }
}
// 呼叫端 (Controller / Main / Service)
public class UserFactory {
public static User createFromConsole() {
Scanner sc = new Scanner(System.in);
System.out.print("輸入姓名: ");
String name = sc.nextLine().trim();
// 可在這裡驗證、預設值、重試邏輯
return new User(name);
}
}
| 維度 | 建構子讀輸入 | 參數化建構子 + Factory |
|---|---|---|
| 可測試性 | 難 (需模擬 System.in) | 易 (直接 `new User("Alice")`) |
| 單一職責 | 違反 (混雜 I/O 與初始化) | 符合 (建構子只初始化) |
| 不可變性 | 難保證 | 易用 `final` 欄位保證 |
| 異常處理 | 建構子拋異常 → 物件半初始化 | Factory 回傳 `Optional |
| 重用性 | 僅限 Console | 支援 GUI、Web、檔案、測試資料等多來源 |
public class User {
private final String name;
private final int age;
private final String email;
private User(Builder b) { this.name=b.name; this.age=b.age; this.email=b.email; }
public static class Builder {
String name; int age; String email;
Builder name(String v){name=v;return this;}
Builder age(int v){age=v;return this;}
Builder email(String v){email=v;return this;}
User build(){ return new User(this); }
}
}
// 使用
User u = new User.Builder().name("Bob").age(25).email("bob@ex.com").build();
遵循「建構子純初始化」原則,代碼即獲得高可測試性、低耦合、易維護三大優勢。