單一原則:方法 對象
策略模式:方法實現
// 策略接口(單一職責:定義計算規范)
public interface PriceStrategy {boolean match(String type); // 職責1:判斷是否適用該策略double calculate(double price); // 職責2:執行計算
}// 具體策略類(每個類只負責一種計算邏輯)
public class VipStrategy implements PriceStrategy {@Overridepublic boolean match(String type) {return "VIP".equals(type);}@Overridepublic double calculate(double price) {return price * 0.8;}
}public class FullReductionStrategy implements PriceStrategy {@Overridepublic boolean match(String type) {return "FULL_REDUCTION".equals(type);}@Overridepublic double calculate(double price) {return price > 100 ? price - 20 : price;}
}// 上下文類(單一職責:路由策略)
public class PriceContext {private List<PriceStrategy> strategies = new ArrayList<>();public PriceContext() {strategies.add(new VipStrategy());strategies.add(new FullReductionStrategy());}public double execute(String type, double price) {return strategies.stream().filter(s -> s.match(type)).findFirst().orElseThrow(() -> new IllegalArgumentException("未知價格策略")).calculate(price);}
}
** 使用工廠模式隔離對象創建**
// 創建邏輯單獨封裝
public class PaymentProcessorFactory {public PaymentProcessor create(String type) {if ("ALIPAY".equals(type)) return new AlipayProcessor();if ("WECHAT".equals(type)) return new WechatProcessor();throw new IllegalArgumentException("未知支付類型");}
}// 使用方保持單一職責
public class OrderService {private PaymentProcessorFactory factory;public void pay(Order order, String paymentType) {PaymentProcessor processor = factory.create(paymentType);processor.process(order.getAmount());}
}
通過裝飾者模式疊加功能
// 基礎接口
public interface DataReader {String read();
}// 基礎實現(單一職責:讀取數據)
public class FileDataReader implements DataReader {public String read() {// 讀取文件內容...}
}// 裝飾器1:增加緩存(單一職責:處理緩存)
public class CachedDataReader implements DataReader {private DataReader wrappee;private String cache;public CachedDataReader(DataReader reader) {this.wrappee = reader;}public String read() {if (cache == null) {cache = wrappee.read();}return cache;}
}// 裝飾器2:增加解密(單一職責:數據解密)
public class DecryptDataReader implements DataReader {private DataReader wrappee;public DecryptDataReader(DataReader reader) {this.wrappee = reader;}public String read() {String data = wrappee.read();return decrypt(data);}
}// 使用組合
DataReader reader = new DecryptDataReader(new CachedDataReader(new FileDataReader()));
1 工廠方法模式(子類創建對象 接口屏蔽細節 支持新增產品)
抽象工廠1接口,具體工廠AB實現接口方法,
服務類 1 factory; if (…) {factory = new FactoryA();} factory調用方法
適用場景:需要創建對象但不確定具體類型時(如多數據源切換、協議適配)。
特點:
- 將對象創建延遲到子類
- 通過接口屏蔽創建細節
- 支持橫向擴展(新增產品類型)
微服務應用示例:多數據庫支持(MySQL/Oracle)
// 抽象工廠
public interface DataSourceFactory {DataSource createDataSource();
}// 具體工廠
public class MySQLDataSourceFactory implements DataSourceFactory {@Overridepublic DataSource createDataSource() {MysqlDataSource dataSource = new MysqlDataSource();dataSource.setURL("jdbc:mysql://localhost:3306/db");return dataSource;}
}public class OracleDataSourceFactory implements DataSourceFactory {@Overridepublic DataSource createDataSource() {OracleDataSource dataSource = new OracleDataSource();dataSource.setURL("jdbc:oracle:thin:@localhost:1521:xe");return dataSource;}
}// 使用工廠(通過環境變量切換)
public class DatabaseService {private DataSource dataSource;public DatabaseService() {String dbType = System.getenv("DB_TYPE");DataSourceFactory factory;if ("oracle".equalsIgnoreCase(dbType)) {factory = new OracleDataSourceFactory();} else {factory = new MySQLDataSourceFactory();}this.dataSource = factory.createDataSource();}
}
2 抽象工廠模式(定義抽象產品和工廠并實現,產品不能不太多)
創建工廠A 抽象產品BC 實現A和BC 選擇工廠
1. 模式定義
抽象工廠模式是一種創建型設計模式,它提供一個接口用于創建相關或依賴對象的家族,而無需指定具體類。與工廠方法模式不同,抽象工廠關注的是產品族的創建。
核心思想:客戶端代碼只與抽象接口交互,完全不知道實際創建的具體產品是什么。
2. 模式結構
角色劃分
角色 | 說明 | 示例代碼 |
---|---|---|
AbstractFactory(抽象工廠) | 聲明創建產品族的方法 | GUIFactory |
ConcreteFactory(具體工廠) | 實現具體產品的創建 | WindowsFactory /MacFactory |
AbstractProduct(抽象產品) | 定義產品接口 | Button /Checkbox |
ConcreteProduct(具體產品) | 實現具體產品類 | WindowsButton /MacCheckbox |
UML類圖
3. 代碼示例:跨平臺UI組件庫
場景需求
開發一個支持Windows和Mac風格的UI庫,需要創建:
- 按鈕(Button)
- 復選框(Checkbox)
(1)抽象產品定義
// 抽象產品:按鈕
public interface Button {void render();void onClick();
}// 抽象產品:復選框
public interface Checkbox {void paint();boolean isChecked();
}
(2)具體產品實現
// Windows風格組件
public class WindowsButton implements Button {@Overridepublic void render() {System.out.println("渲染Windows風格按鈕");}@Overridepublic void onClick() {System.out.println("Windows按鈕點擊事件");}
}public class WindowsCheckbox implements Checkbox {@Overridepublic void paint() {System.out.println("繪制Windows風格復選框");}@Overridepublic boolean isChecked() {return true;}
}// Mac風格組件
public class MacButton implements Button {@Overridepublic void render() {System.out.println("渲染Mac風格按鈕");}@Overridepublic void onClick() {System.out.println("Mac按鈕點擊事件");}
}public class MacCheckbox implements Checkbox {@Overridepublic void paint() {System.out.println("繪制Mac風格復選框");}@Overridepublic boolean isChecked() {return false;}
}
(3)抽象工廠定義
public interface GUIFactory {Button createButton();Checkbox createCheckbox();
}
(4)具體工廠實現
// Windows工廠
public class WindowsFactory implements GUIFactory {@Overridepublic Button createButton() {return new WindowsButton();}@Overridepublic Checkbox createCheckbox() {return new WindowsCheckbox();}
}// Mac工廠
public class MacFactory implements GUIFactory {@Overridepublic Button createButton() {return new MacButton();}@Overridepublic Checkbox createCheckbox() {return new MacCheckbox();}
}
(5)客戶端代碼
public class Application {private Button button;private Checkbox checkbox;public Application(GUIFactory factory) {this.button = factory.createButton();this.checkbox = factory.createCheckbox();}public void paint() {button.render();checkbox.paint();}public static void main(String[] args) {// 根據配置選擇工廠GUIFactory factory;if (System.getProperty("os.name").contains("Windows")) {factory = new WindowsFactory();} else {factory = new MacFactory();}Application app = new Application(factory);app.paint();}
}
4. 模式優勢
優點:
- 確保產品兼容性:同一工廠創建的對象屬于同一產品族
- 客戶端與具體實現解耦:只依賴抽象接口
- 易于擴展新產品族:新增風格只需增加新工廠類
缺點:
- 增加新產品類型困難:需要修改所有工廠接口(違反開閉原則)
- 類數量膨脹:每增加一個產品族需要新增多個類
5. 經典應用場景
- 跨平臺UI框架(如Java AWT/Swing)
- 數據庫訪問層(支持多種數據庫:MySQL/Oracle)
- 游戲開發(不同風格的角色/道具生成)
- 主題系統(白天/黑夜模式切換)
6. 模式對比
(1)抽象工廠 vs 工廠方法
維度 | 抽象工廠 | 工廠方法 |
---|---|---|
關注點 | 產品族創建 | 單一產品創建 |
復雜度 | 高(需要管理多個產品等級) | 低 |
擴展方向 | 橫向擴展(新增產品族) | 縱向擴展(新增產品類型) |
(2)抽象工廠 vs 建造者模式 |
- 抽象工廠:立即返回組成產品族的各個對象
- 建造者:分步驟構建復雜單一對象
7. Spring框架中的實踐
Spring的FactoryBean
接口與抽象工廠模式思想相似:
public interface FactoryBean<T> {T getObject() throws Exception; // 相當于createProduct()Class<?> getObjectType();boolean isSingleton();
}// 使用示例
@Bean
public FactoryBean<DataSource> dataSource() {return new AbstractFactoryBean<DataSource>() {@Overridepublic Class<?> getObjectType() {return DataSource.class;}@Overrideprotected DataSource createInstance() {// 返回具體數據源實現return new HikariDataSource();}};
}
8. 最佳實踐建議
- 合理控制產品族規模:避免一個工廠需要創建過多產品
- 考慮使用依賴注入(如Spring)來管理工廠實例
- 與原型模式結合:當產品創建成本高時,可以用原型減少new操作
- 明確產品族邊界:不同產品族之間不應有隱含依賴
3 模板(代碼少 父類掌握流程 有鉤子)
、
抽象類 靜態方法寫步驟1234,拆分具體步驟,固定/抽象/鉤子都行
經典案例
1. Java 集合框架
- AbstractList
的 addAll()
方法調用 add()
(子類如 ArrayList
實現 add()
)。
2. Servlet 生命周期
- HttpServlet
的 service()
方法調用 doGet()
或 doPost()
。
3. Sp ring 框架
- JdbcTemplate
的 execute()
方法定義數據庫操作流程,具體 SQL 由回調接口實現。
public abstract class AbstractClass {// 模板方法(final防止子類覆蓋算法結構)public final void templateMethod() {step1(); // 固定步驟step2(); // 可變步驟(子類實現)if (hookMethod()) { // 鉤子方法(可選擴展點)step3(); }}// 固定實現(所有子類共用)private void step1() {System.out.println("執行固定步驟1");}// 抽象方法(必須由子類實現)protected abstract void step2();// 鉤子方法(可選覆蓋,提供默認實現)protected boolean hookMethod() {return true;}// 可選步驟(默認空實現)protected void step3() {}}
優勢:
- 代碼復用:公共邏輯集中在父類
- 擴展可控:通過鉤子方法提供靈活擴展點
- 反向控制:父類掌控流程,子類專注實現
劣勢:
- 繼承強耦合:子類必須依賴父類
- 違反開閉原則:新增步驟可能需要修改父類
- 方法爆炸:過多抽象方法會增加子類負擔
建議
- 模板方法盡量簡短(不超過10行)
- 命名顯式化:
- 模板方法用
processXXX()
/executeXXX()
- 步驟方法用
doXXX()
/performXXX()
- 模板方法用
- 謹慎使用繼承:如果變化點過多,考慮改用策略模式
- 合理使用鉤子方法:避免過度設計,只在真正需要擴展點時使用
4 策略模式 (新增不用改舊代碼 支付)
模式優勢
優點:
開閉原則:新增策略無需修改現有代碼
消除條件語句:替代大量的if-else/switch-case
運行時靈活切換:動態改變對象行為
算法復用:不同上下文可共享策略對象
缺點:
客戶端必須了解所有策略:需要知道不同策略的區別
對象數量增加:每個策略都是一個類
通信開銷:策略與上下文可能需要交換數據
經典應用場景
支付系統(如示例)
排序算法(快速排序/歸并排序動態切換)
游戲AI(不同難度級別的敵人行為)
物流計算(不同快遞公司的運費計算)
Spring資源訪問(Resource
接口的不同實現)