許多人以為介面是為了補足 Java 無多重繼承的缺憾。**錯了。** 介面與繼承是完全不同維度的概念:
OO 精髓在於抽象,介面是抽象的極致體現——**只定義「做什麼」,完全不關心「怎麼做」**。
package org.bromon.test;
public interface DB {
Connection openDB(String url, String user, String password);
void close();
}
public class MySQL implements DB {
private String url = "jdbc:mysql://localhost:3306/test";
private String user = "root";
private String password = "";
private Connection conn;
@Override
public Connection openDB(String url, String user, String password) {
// 實際連線邏輯
return conn;
}
@Override public void close() { /* 關閉邏輯 */ }
}
同理可有 Oracle、SQLServer、DB2 等實作。
// ❌ 錯誤:介面無法實例化
// DB db = new DB();
// ✅ 正確:但寫死具體類別,違反開閉原則
// DB db = new MySQL();
// 🎯 最佳:工廠模式解耦實例化
public class DBFactory {
public static DB getConnection() {
// 可從配置檔、環境變數、SPI 動態決定
return new MySQL();
}
}
// 使用端
DB db = DBFactory.getConnection();
db.openDB(...);
DB 介面,完全不知道底層是 MySQL 還是 Oracle。**繼承的本意是抽象,不是複用。**
// ❌ 錯誤:為了複用 run() 而繼承
class B extends A { }
// ✅ 正確:組合/委派複用
class B {
private A a = new A();
public void run() { a.run(); }
}
// ✅ 正確:抽象類別定義模板,子類填充細節
abstract class Template {
public final void process() {
step1();
abstractStep2(); // 子類實作
step3();
}
abstract void abstractStep2();
}
GoF 設計模式核心原則:**多用組合,少用繼承**。Java 禁止多重繼承正是為了強制開發者思考「是否真的需要 is-a 關係」。
| 場景 | 推薦 | 理由 |
|---|---|---|
| 定義純契約、多實作、跨層解耦 | 介面 | 零實作、多實現、支援多介面實作 |
| 共享部分實作、定義模板流程、提供預設行為 | 抽象類別 | 可含非抽象方法、建構子、狀態 |
| 最終業務實體、無需被繼承、不可變物件 | final 具體類別 | 禁止繼承、編譯器優化、線程安全 |
介面劃定邊界,繼承共享骨架,組合裝配行為。 寫代碼前先問:
1. 這是「是一種」還是「能做某事」?
2. 我要複用代碼,還是定義契約?
3. 變更點在哪裡?如何將變更隔離在工廠/策略/模板中?
搞懂這三點,架構就不會爛。