面向物件設計不僅是語法糖,更是一套管理複雜度的工程學規範。以下四大原則(統稱 SOLID 中的 O、L、I、D)是構建高內聚、低耦合、可擴展系統的基石。
定義: 軟體實體(類別、模組、函式)應該對擴展開放,對修改關閉。
早期設計若為每種收音機(自動搜台、按鍵式、數位式)寫獨立類別並重載方法,新增類型需修改既有代碼,違反 OCP。
// 抽象層 - 穩定、不變
public interface Radio {
void powerOn();
void powerOff();
void increaseFrequency();
void decreaseFrequency();
void increaseVolume();
void decreaseVolume();
}
// 具體實現 - 可自由擴展
public class AnalogRadio implements Radio { /* ... */ }
public class DigitalRadio implements Radio { /* ... */ }
public class InternetRadio implements Radio { /* ... */ }
// 客戶端代碼 - 僅依賴介面
public class RadioUser {
private Radio radio;
public RadioUser(Radio radio) { this.radio = radio; }
public void tune() { radio.increaseFrequency(); }
}
效益: 新增 SatelliteRadio 只需實作 Radio 介面,完全不動既有代碼;修改某具體實現也不影響其他類別。
定義: 子類別必須能夠替換其父類別,且不影響程式正確性。即:
IS-A關係必須代表行為相容,而非單純結構繼承。
// 違反 LSP
class Rectangle {
protected int width, height;
public void setWidth(int w) { width = w; }
public void setHeight(int h) { height = h; }
public int area() { return width * height; }
}
class Square extends Rectangle {
@Override public void setWidth(int w) { width = height = w; }
@Override public void setHeight(int h) { width = height = h; }
}
// 客戶端代碼
void resize(Rectangle r) {
r.setWidth(10);
r.setHeight(5);
assert r.area() == 50; // 若傳入 Square,斷言失敗!
}
Square 破壞了 Rectangle 的行為契約(寬高可獨立變化),導致替換失敗。
interface Shape { int area(); }
class Rectangle implements Shape { /* 獨立實現 */ }
class Square implements Shape { /* 獨立實現 */ }
// 無繼承關係,僅共享介面契約
定義: 高層模組不應依賴低層模組,兩者皆應依賴抽象;抽象不應依賴細節,細節應依賴抽象。
| 維度 | 結構化(傳統) | 面向物件(DIP) |
|---|---|---|
| 高層模組 | 直接呼叫低層具體類 | 依賴抽象介面 |
| 低層模組 | 被動被呼叫 | 實作抽象介面 |
| 變更影響 | 低層變動波及高層 | 低層可自由替換 |
| 測試性 | 難以 Mock | 易於注入測試雙工 |
// 抽象層
interface PaymentGateway {
boolean charge(String orderId, double amount);
}
// 低層實現
class AlipayGateway implements PaymentGateway { /* ... */ }
class StripeGateway implements PaymentGateway { /* ... */ }
// 高層模組 - 僅依賴介面
class OrderService {
private final PaymentGateway gateway;
public OrderService(PaymentGateway gateway) { // 建構子注入
this.gateway = gateway;
}
public void checkout(String orderId, double amount) {
gateway.charge(orderId, amount);
}
}
切換支付通道只需替換注入的實現類,OrderService 零修改。
定義: 客戶不應被迫依賴它不使用的方法。多個專用介面優於單一臃腫介面。
// 違反:強迫所有實現類覆寫不需要的方法
interface Machine {
void print();
void scan();
void fax();
void print3D();
}
class OldPrinter implements Machine {
public void print() { /* ... */ }
public void scan() { throw new UnsupportedOperationException(); }
public void fax() { throw new UnsupportedOperationException(); }
public void print3D() { throw new UnsupportedOperationException(); }
}
interface Printer { void print(); }
interface Scanner { void scan(); }
interface Fax { void fax(); }
interface Printer3D { void print3D(); }
// 組合使用
class MultiFunctionDevice implements Printer, Scanner, Fax { /* ... */ }
class SimplePrinter implements Printer { /* ... */ }
這四原則在幾何分布上呈現樹狀拓撲:
共同目標:降低耦合度、控制設計複雜度、避免循環依賴與閉包結構。
掌握並內化這四大原則,是從「會寫代碼」進階為「軟體架構師」的關鍵里程碑。