文章目錄
- 什么是設計模式?
- 為什么使用設計模式?
- 設計模式的核心設計原則是什么?
- 1. 開閉原則(Open-Closed Principle, OCP)
- 2. 里氏替換原則(Liskov Substitution Principle, LSP)
- 3. 依賴倒置原則(Dependency Inversion Principle, DIP)
- 4. 單一職責原則(Single Responsibility Principle, SRP)
- 5. 接口隔離原則(Interface Segregation Principle, ISP)
- 6. 迪米特法則(Law of Demeter, LoD)
- 7. 合成/聚合復用原則(Composite Reuse Principle, CRP)
- 七大原則的核心價值總結
- 常見的設計模式有哪些?
作為一個Java開發程序員,設計模式就像是習武之人的內功心法,直接少走20年彎路,其重要性顯而易見。通過掌握7大設計原則、23種設計模式,可以編寫出 高內聚、低耦合、易擴展、易維護 的高質量代碼,應對復雜軟件系統的設計。
什么是設計模式?
-
設計模式,是一套被反復使用、多數人知曉的、經過分類編目的代碼設計經驗的總結。
-
他描述了在軟件設計過程中的一些不斷重復出現的問題,以及該問題的解決方案。也就是說,他是解決特定問題的一系列套路,是前輩們的代碼設計經驗的總結,具有一定的普遍性,可以反復使用。
為什么使用設計模式?
- 設計模式的本質是面向對象設計原則的實際運用,是對類的封裝性、繼承性和多態性以及類的關聯關系和組合關系的充分理解。
- 正確使用設計模式具有以下優點:
- 可以提高程序員的思維能力、編程能力和設計能來。
- 使程序設計更加標準化、代碼編程更加工程化,使軟件開發效率大大提高,從而縮短軟件開發的周期。
- 使設計的代碼可重用性高、可讀性強、可靠性高、靈活性好、可維護性強。
設計模式的核心設計原則是什么?
1. 開閉原則(Open-Closed Principle, OCP)
-
定義:軟件實體(類、模塊、函數等)應對 擴展開放,對 修改關閉。
- 擴展開放:通過新增代碼(如新類、新模塊)來實現功能擴展。
- 修改關閉:不修改已有代碼即可滿足新需求。
-
核心思想
- 抽象約束:通過接口或抽象類構建穩定的抽象層,隔離變化點。
- 封裝變化:將可變因素封裝在具體實現類中,需求變更時只需新增或調整實現類。
-
示例
// 抽象接口(穩定層) interface Shape {void draw(); }// 具體實現類(可擴展) class Circle implements Shape {public void draw() {System.out.println("Drawing Circle");} }class Rectangle implements Shape {public void draw() {System.out.println("Drawing Rectangle");} }// 客戶端代碼(無需修改) public class Main {public static void main(String[] args) {Shape shape = new Circle();shape.draw(); // 輸出:Drawing Circle} }
-
好處
- 降低維護成本:無需修改已有代碼,減少引入新錯誤的風險。
- 提高擴展性:通過新增實現類即可支持新功能(如新增
Triangle
類)。 - 增強穩定性:抽象層保持不變,系統架構更穩定。
2. 里氏替換原則(Liskov Substitution Principle, LSP)
-
定義:所有引用基類的地方必須能透明地使用其子類的對象。
- 子類替換父類:子類對象應能完全替代父類對象,且程序行為和結果不受影響。
-
核心思想
- 行為一致性:子類應繼承父類的契約,不破壞父類定義的規范。
- 避免副作用:子類不應強制改變父類的行為預期。
-
示例
// 父類 abstract class Vehicle {abstract void start(); }// 子類(符合 LSP) class Car extends Vehicle {public void start() {System.out.println("Car starts");} }// 錯誤示例(違反 LSP) class BrokenCar extends Vehicle {public void start() {System.out.println("Car explodes"); // 破壞父類行為預期} }
-
好處
- 代碼復用性:子類可安全復用父類邏輯。
- 可靠性:程序行為更可預測,避免因子類錯誤導致異常。
- 解耦:高層模塊無需關注具體子類實現。
3. 依賴倒置原則(Dependency Inversion Principle, DIP)
-
定義
- 高層模塊不應該依賴低層模塊,二者都應該依賴 抽象。
- 抽象不應該依賴細節,細節應該依賴抽象。
-
核心思想
- 面向接口編程:通過抽象(接口或抽象類)解耦模塊間的依賴關系。
- 減少耦合:高層模塊不直接依賴底層實現,而是通過抽象接口間接調用。
-
示例
// 抽象接口(高層依賴) interface Database {void save(); }// 具體實現(低層模塊) class MySQL implements Database {public void save() {System.out.println("Saving to MySQL");} }// 高層模塊(依賴抽象) class UserService {private Database database;public UserService(Database database) {this.database = database;}public void saveUser() {database.save();} }
-
好處
- 靈活性:可輕松切換底層實現(如從 MySQL 改為 PostgreSQL)。
- 測試性:通過 Mock 抽象接口,方便單元測試。
- 解耦:模塊間依賴關系更清晰,降低維護成本。
4. 單一職責原則(Single Responsibility Principle, SRP)
-
定義:一個類應該只有一個引起它變化的原因。
- 職責分離:一個類只負責一項功能,避免功能耦合。
-
核心思想
- 高內聚:將相關功能集中在一個類中。
- 低耦合:不同職責分離到獨立類中,減少相互影響。
-
示例
// 錯誤示例(違反 SRP) class User {private String name;private String email;// 職責1:用戶信息管理public void setName(String name) { this.name = name; }// 職責2:郵件發送public void sendEmail(String message) {System.out.println("Sending email to " + email + ": " + message);} }// 改進方案(職責分離) class User {private String name;private String email;public void setName(String name) { this.name = name; } }class EmailService {public void sendEmail(String email, String message) {System.out.println("Sending email to " + email + ": " + message);} }
-
好處
- 易維護:修改一個功能時,不會影響其他職責。
- 復用性:單一職責的類更容易被其他模塊復用。
- 可測試性:職責清晰的類更易編寫單元測試。
5. 接口隔離原則(Interface Segregation Principle, ISP)
-
定義:客戶端不應該依賴它不需要的接口。
- 接口小型化:提供多個細粒度的接口,避免“胖接口”。
-
核心思想
- 按需依賴:客戶端僅依賴其實際需要的方法。
- 避免冗余:減少接口中不必要方法的暴露。
-
示例
// 錯誤示例(“胖”接口) interface Animal {void eat(); // 所有動物都需要void fly(); // 僅鳥類需要void swim(); // 僅魚類需要 }// 改進方案(接口隔離) interface Eatable {void eat(); }interface Flyable {void fly(); }interface Swimmable {void swim(); }class Bird implements Eatable, Flyable {public void eat() { System.out.println("Bird eats"); }public void fly() { System.out.println("Bird flies"); } }class Fish implements Eatable, Swimmable {public void eat() { System.out.println("Fish eats"); }public void swim() { System.out.println("Fish swims"); } }
-
好處
- 減少依賴:客戶端僅需關注所需接口。
- 靈活性:接口組合更靈活,適應不同需求。
- 可擴展性:新增功能時,只需擴展特定接口。
6. 迪米特法則(Law of Demeter, LoD)
-
定義:一個對象應盡可能少地了解其他對象。
- 最少知識原則:只與直接朋友通信,避免跨層依賴。
-
核心思想
- 降低耦合:對象之間交互僅限于必要的依賴。
- 封裝細節:隱藏內部實現,通過接口暴露行為。
-
示例
// 錯誤示例(違反 LoD) class Manager {public void manage(Employee employee) {System.out.println("Manager manages employee: " + employee.getName());} }class Employee {private String name;public String getName() { return name; } }class Client {public void doWork() {Employee employee = new Employee();Manager manager = new Manager();manager.manage(employee); // 正確:Manager 直接依賴 Employee} }// 更復雜的錯誤示例(跨層依賴) class Client {public void doWork() {Department department = new Department();Employee employee = department.getEmployee(0);Manager manager = new Manager();manager.manage(employee); // 錯誤:Client 間接依賴 Employee} }
-
好處
- 松耦合:模塊間依賴更清晰,減少連鎖修改。
- 可維護性:代碼結構更簡潔,易于理解和調試。
- 穩定性:減少因依賴變更導致的連鎖反應。
7. 合成/聚合復用原則(Composite Reuse Principle, CRP)
-
定義:盡量使用 對象組合/聚合,而不是繼承來達到復用目的。
- 組合優先于繼承:通過組合實現功能擴展,避免繼承的強耦合。
-
核心思想
- 靈活性:組合允許動態替換實現,繼承是靜態的。
- 減少繼承層級:避免多層繼承導致的復雜性和脆弱性。
-
示例
// 錯誤示例(繼承) class Car {void start() { System.out.println("Car starts"); } }class SportsCar extends Car {void start() { System.out.println("SportsCar starts with V8 engine"); } }// 改進方案(組合) interface Engine {void start(); }class V8Engine implements Engine {public void start() { System.out.println("V8 Engine starts"); } }class Car {private Engine engine;public Car(Engine engine) {this.engine = engine;}void start() {engine.start(); // 通過組合調用} }
-
好處
- 靈活性:可動態切換實現(如
Car
支持多種Engine
)。 - 降低耦合:避免繼承導致的強依賴關系。
- 代碼復用性:通過組合復用多個獨立組件。
- 靈活性:可動態切換實現(如
七大原則的核心價值總結
原則名稱 | 核心目標 | 關鍵實踐 |
---|---|---|
開閉原則 | 對擴展開放,對修改關閉 | 抽象層封裝變化 |
里氏替換 | 子類替換父類不影響程序行為 | 遵循父類契約 |
依賴倒置 | 高層依賴抽象,低層實現細節 | 面向接口編程 |
單一職責 | 一個類只負責一項職責 | 職責分離,高內聚 |
接口隔離 | 提供最小接口,避免冗余 | 細粒度接口,按需依賴 |
迪米特法則 | 減少對象間直接交互 | 封裝細節,最少知識 |
合成復用 | 優先組合而非繼承 | 通過組合實現靈活擴展 |
常見的設計模式有哪些?
設計模式類型 | 設計模式名稱 | 核心作用 | 適用場景 |
---|---|---|---|
創建型 | 單例模式 | 保證唯一實例 | 資源管理、全局訪問 |
創建型 | 工廠方法 | 解耦對象創建 | 動態選擇實現 |
創建型 | 抽象工廠 | 創建相關對象族 | 跨平臺UI、產品族生成 |
創建型 | 建造者 | 分階段構建復雜對象 | 配置復雜對象 |
創建型 | 原型 | 復制現有對象 | 高性能對象創建 |
結構型 | 適配器 | 兼容接口 | 集成遺留系統 |
結構型 | 裝飾器 | 動態擴展功能 | 功能組合 |
結構型 | 代理 | 控制訪問 | 權限控制、延遲加載 |
結構型 | 組合 | 樹形結構 | 文件系統、菜單 |
結構型 | 橋接 | 解耦抽象與實現 | 多維變化系統 |
結構型 | 外觀 | 簡化接口 | 復雜系統簡化 |
結構型 | 享元 | 共享對象 | 內存優化 |
行為型 | 策略 | 動態切換算法 | 支付方式、排序算法 |
行為型 | 觀察者 | 事件通知 | 消息訂閱、GUI事件 |
行為型 | 命令 | 封裝請求 | 撤銷/重做、任務隊列 |
行為型 | 模板方法 | 定義算法骨架 | 測試框架、流程固定 |
行為型 | 迭代器 | 遍歷集合 | 統一訪問不同數據結構 |
行為型 | 責任鏈 | 傳遞請求 | 審批流程、過濾器鏈 |
行為型 | 備忘錄 | 恢復狀態 | 撤銷操作、狀態快照 |
行為型 | 狀態 | 狀態驅動行為 | 訂單狀態機、游戲狀態 |
行為型 | 訪問者 | 分離操作與數據結構 | 編譯器、數據分析 |
行為型 | 中介者 | 減少對象間依賴 | 聊天室、協調復雜交互 |
行為型 | 解釋器 | 解析語言 | 正則表達式、自定義DSL |
關于設計模式的詳細內容將在后續專門介紹,如需了解,可以關注一下后續文章。