設計模式是「經驗的總結」,而六大原則是設計模式的「理論基石」。掌握這六大原則,才能在具體場景中靈活選擇、組合模式,寫出**高內聚、低耦合、易擴展、可維護**的軟體。
一個類應該只有一個引起它變化的原因,即**只負責一項職責**。
class Calculator {
public int add(String filePath) throws IOException {
// 1. 讀取檔案
BufferedReader br = new BufferedReader(new FileReader(filePath));
int a = Integer.parseInt(br.readLine());
int b = Integer.parseInt(br.readLine());
// 2. 計算
return a + b;
}
}
問題:`Calculator` 同時負責 **IO 讀取** 與 **計算邏輯**,變更檔案格式或計算規則都會修改此類。
// 職責 1:資料讀取
class FileNumberReader {
public Pair readTwoNumbers(String path) throws IOException {
try (BufferedReader br = new BufferedReader(new FileReader(path))) {
return new Pair<>(Integer.parseInt(br.readLine()), Integer.parseInt(br.readLine()));
}
}
}
// 職責 2:計算邏輯
class Calculator {
public int add(int a, int b) { return a + b; }
public int subtract(int a, int b) { return a - b; }
public int multiply(int a, int b) { return a * b; }
}
// 使用
Pair nums = new FileNumberReader().readTwoNumbers("data.txt");
int result = new Calculator().add(nums.getFirst(), nums.getSecond());
子類型必須能夠替換其基類型,**且不影響程式正確性**。即:`Father f = new Son();` 任何使用 `Father` 的地方,換成 `Son` 都不應破壞行為契約。
class Bird {
public void fly() { System.out.println("飛行"); }
}
class Penguin extends Bird {
@Override
public void fly() { throw new UnsupportedOperationException("企鵝不會飛"); } // 破壞契約
}
使用 `Bird b = new Penguin(); b.fly();` 拋出異常,違反 LSP。
interface Flyable { void fly(); }
interface Swimmable { void swim(); }
class Sparrow implements Flyable { public void fly() { ... } }
class Penguin implements Swimmable { public void swim() { ... } }
// 不再強迫 Penguin 繼承 fly()
**子類只擴展功能,不改變父類行為契約** (前置條件不強於父類,後置條件不弱於父類)。
客戶端不應依賴它不需要的介面。**介面應盡量細小、專一**,避免「胖介面」。
interface MobilePhone {
void call();
void sendSms();
void playGame(); // 老人機不需要
void browseWeb(); // 功能機不需要
}
interface BasicPhone { void call(); void sendSms(); }
interface SmartPhone extends BasicPhone { void playGame(); void browseWeb(); }
interface CameraPhone { void takePhoto(); }
// 實作類只實作需要的介面
class FeaturePhone implements BasicPhone { ... }
class IPhone implements SmartPhone, CameraPhone { ... }
- 高層模組不應依賴低層模組,**兩者都應依賴抽象** (介面/抽象類)。
- 抽象不應依賴細節,**細節應依賴抽象**。
class MySQLDatabase { public void save(String data) { ... } }
class UserService {
private MySQLDatabase db = new MySQLDatabase(); // 緊耦合
public void register(String user) { db.save(user); }
}
換資料庫需修改 `UserService`,違反 DIP。
interface Database { void save(String data); }
class MySQLDatabase implements Database { public void save(String data) { ... } }
class PostgreSQLDatabase implements Database { public void save(String data) { ... } }
class UserService {
private final Database database; // 依賴抽象
public UserService(Database database) { this.database = database; } // 建構子注入
public void register(String user) { database.save(user); }
}
// 組裝 (由外部容器/主程式決定具體實作)
UserService service = new UserService(new PostgreSQLDatabase());
一個物件應盡可能少地瞭解其他物件。**只與直接朋友 (依賴注入的、建立的、參數傳入的) 通訊,不與陌生人 (朋友的朋友) 通訊。**
class Company {
Department getDepartment() { ... }
}
class Department {
Employee getManager() { ... }
}
class Employee {
String getName() { ... }
}
// 客戶端違反 LoD
String name = company.getDepartment().getManager().getName(); // 穿透 3 層
class Company {
Department getDepartment() { ... }
String getDepartmentManagerName() { // 封裝內部導航
return getDepartment().getManager().getName();
}
}
// 客戶端只知 Company
String name = company.getDepartmentManagerName();
**對擴展開放,對修改關閉**。即:新增功能時,**不修改既有原始碼**,而是透過新增程式碼 (繼承、實作介面、組合) 實現。
class ReportGenerator {
public void generate(String type) {
if ("PDF".equals(type)) { ... }
else if ("EXCEL".equals(type)) { ... }
// 新增 Word 需修改此方法 → 違反 OCP
}
}
interface ReportExporter { void export(ReportData data); }
class PdfExporter implements ReportExporter { public void export(ReportData data) { ... } }
class ExcelExporter implements ReportExporter { public void export(ReportData data) { ... } }
class WordExporter implements ReportExporter { public void export(ReportData data) { ... } } // 新增不動舊碼
class ReportGenerator {
private final Map exporters; // 注入所有策略
public void generate(String type, ReportData data) {
exporters.get(type).export(data);
}
}
| 原則 | 核心關鍵字 | 主要解決問題 | 常配合模式 |
|---|---|---|---|
| SRP | 單一職責 | 類膨脹、職責混雜 | 單例、工廠 |
| LSP | 子類可替換父類 | 繼承濫用、契約破壞 | 模板方法、策略 |
| ISP | 介面細小專一 | 胖介面、空實作 | 介面隔離、適配器 |
| DIP | 依賴抽象、DI | 高層耦合低層、難測試 | 依賴注入、工廠、策略 |
| LoD | 最少知識 | 過度耦合、鏈式呼叫 | 外觀、中介者 |
| OCP | 擴展開放、修改關閉 | 頻繁修改舊碼、回歸風險 | 策略、裝飾者、觀察者 |
六大原則不是教條,而是**設計決策的指南針**。實務中常需權衡 (如過度遵循 OCP 導致類爆炸),但內化這些原則,能讓你在面對複雜需求時,做出**可預測、可維護、可演進**的架構選擇。建議從 **SRP + DIP** 入手,配合 **依賴注入框架 (Spring/CDI)** 落地,再逐步應用其他原則重構既有代碼。