Java常用設計模式完整指南(含詳細場景與實例)

Java常用設計模式完整指南(含詳細場景與實例)


一、創(chuàng)建型模式

1. 單例模式(Singleton)

具體應用場景與實例

應用場景 具體例子 為什么用單例
數(shù)據(jù)庫連接池 HikariCP、Druid、C3P0 連接池管理全局數(shù)據(jù)庫連接,避免頻繁創(chuàng)建銷毀
線程池 Executors創(chuàng)建的各類線程池 統(tǒng)一管理線程資源,控制并發(fā)數(shù)
配置中心 Spring的Environment、Apollo客戶端 全局統(tǒng)一配置,避免重復讀取配置文件
緩存管理 EhCache、Caffeine的CacheManager 全局緩存實例,統(tǒng)一管理緩存生命周期
日志框架 Log4j的Logger、SLF4J的LoggerFactory 全局日志配置,統(tǒng)一輸出格式和級別
Spring Bean 默認Scope為singleton的Bean 容器管理的單例,節(jié)省內(nèi)存,避免重復初始化
運行時環(huán)境 Runtime.getRuntime() JVM全局唯一運行時實例

實際代碼示例

// ========== 數(shù)據(jù)庫連接池單例(模擬HikariCP)==========
public class DataSourceManager {
    private static final HikariDataSource DATA_SOURCE;
    
    static {
        // 類加載時初始化,JVM保證線程安全
        HikariConfig config = new HikariConfig();
        config.setJdbcUrl("jdbc:mysql://localhost:3306/mydb");
        config.setUsername("root");
        config.setPassword("password");
        config.setMaximumPoolSize(20);  // 最大連接數(shù)
        DATA_SOURCE = new HikariDataSource(config);
    }
    
    private DataSourceManager() {}
    
    public static Connection getConnection() throws SQLException {
        return DATA_SOURCE.getConnection();
    }
    
    public static void close() {
        DATA_SOURCE.close();
    }
}

// 使用:全局獲取連接
Connection conn = DataSourceManager.getConnection();
// ========== 配置中心單例(模擬Apollo)==========
public class AppConfig {
    private static final AppConfig INSTANCE = new AppConfig();
    private Properties properties = new Properties();
    
    private AppConfig() {
        // 加載配置文件
        loadFromApollo();
    }
    
    public static AppConfig getInstance() {
        return INSTANCE;
    }
    
    public String getString(String key) {
        return properties.getProperty(key);
    }
    
    public int getInt(String key) {
        return Integer.parseInt(properties.getProperty(key));
    }
    
    private void loadFromApollo() {
        // 模擬從Apollo配置中心拉取配置
        properties.setProperty("server.port", "8080");
        properties.setProperty("redis.host", "localhost");
        properties.setProperty("thread.pool.size", "10");
    }
}

// 使用:全局統(tǒng)一訪問配置
int port = AppConfig.getInstance().getInt("server.port");
String redisHost = AppConfig.getInstance().getString("redis.host");

2. 工廠模式(Factory)

具體應用場景與實例

應用場景 具體例子 為什么用工廠
支付系統(tǒng) 支付寶、微信、銀聯(lián)、Apple Pay 統(tǒng)一支付入口,支持新支付方式熱插拔
日志框架 Log4j、Logback、JDK Logging 統(tǒng)一日志API,底層實現(xiàn)可切換
數(shù)據(jù)庫方言 MyBatis的Dialect、Hibernate方言 適配不同數(shù)據(jù)庫(MySQL/Oracle/PGSQL)
文檔導出 PDF、Excel、Word導出 統(tǒng)一導出接口,支持新格式擴展
消息推送 短信、郵件、App推送、微信模板消息 統(tǒng)一推送接口,根據(jù)場景選擇渠道
Spring Bean創(chuàng)建 BeanFactoryFactoryBean 容器統(tǒng)一管理對象生命周期
線程創(chuàng)建 ThreadFactory 統(tǒng)一線程命名、優(yōu)先級、異常處理

實際代碼示例

// ========== 支付系統(tǒng)工廠(實際項目常用)==========
public interface PaymentChannel {
    PayResult pay(Order order);
    PayResult query(String orderId);
    PayResult refund(String orderId, BigDecimal amount);
}

// 支付寶實現(xiàn)
public class AlipayChannel implements PaymentChannel {
    private AlipayClient alipayClient;  // 支付寶SDK客戶端
    
    public PayResult pay(Order order) {
        AlipayTradePayRequest request = new AlipayTradePayRequest();
        request.setBizContent("...");
        AlipayTradePayResponse response = alipayClient.execute(request);
        return new PayResult(response.isSuccess(), response.getTradeNo());
    }
    // ... query和refund實現(xiàn)
}

// 微信支付實現(xiàn)
public class WechatPayChannel implements PaymentChannel {
    private WXPay wxPay;  // 微信SDK
    
    public PayResult pay(Order order) {
        Map<String, String> resp = wxPay.unifiedOrder(buildParams(order));
        return new PayResult("SUCCESS".equals(resp.get("return_code")), resp.get("prepay_id"));
    }
}

// 支付工廠 - 根據(jù)支付方式創(chuàng)建對應渠道
public class PaymentFactory {
    private static final Map<String, PaymentChannel> CHANNELS = new ConcurrentHashMap<>();
    
    static {
        CHANNELS.put("ALIPAY", new AlipayChannel());
        CHANNELS.put("WECHAT", new WechatPayChannel());
        CHANNELS.put("UNIONPAY", new UnionPayChannel());
    }
    
    public static PaymentChannel getChannel(String payType) {
        PaymentChannel channel = CHANNELS.get(payType.toUpperCase());
        if (channel == null) {
            throw new UnsupportedPaymentException("不支持的支付方式: " + payType);
        }
        return channel;
    }
    
    // 支持動態(tài)注冊新支付方式(插件化)
    public static void registerChannel(String type, PaymentChannel channel) {
        CHANNELS.put(type.toUpperCase(), channel);
    }
}

// 使用:統(tǒng)一支付入口
public class OrderService {
    public PayResult processPayment(Order order) {
        // 根據(jù)用戶選擇獲取對應支付渠道
        PaymentChannel channel = PaymentFactory.getChannel(order.getPayType());
        return channel.pay(order);
    }
}
// ========== 文檔導出工廠(報表系統(tǒng)常用)==========
public interface DocumentExporter {
    void export(List<DataRow> data, OutputStream out);
    String getContentType();
    String getFileExtension();
}

// Excel導出
public class ExcelExporter implements DocumentExporter {
    public void export(List<DataRow> data, OutputStream out) {
        Workbook workbook = new XSSFWorkbook();
        Sheet sheet = workbook.createSheet("數(shù)據(jù)");
        // ... 填充數(shù)據(jù)
        workbook.write(out);
    }
    public String getContentType() { return "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"; }
    public String getFileExtension() { return "xlsx"; }
}

// PDF導出
public class PdfExporter implements DocumentExporter {
    public void export(List<DataRow> data, OutputStream out) {
        Document document = new Document();
        PdfWriter.getInstance(document, out);
        // ... 生成PDF表格
    }
    public String getContentType() { return "application/pdf"; }
    public String getFileExtension() { return "pdf"; }
}

// CSV導出
public class CsvExporter implements DocumentExporter {
    public void export(List<DataRow> data, OutputStream out) {
        try (PrintWriter writer = new PrintWriter(out)) {
            // 寫入CSV頭
            writer.println("姓名,年齡,部門");
            // 寫入數(shù)據(jù)
            for (DataRow row : data) {
                writer.printf("%s,%d,%s%n", row.getName(), row.getAge(), row.getDept());
            }
        }
    }
    public String getContentType() { return "text/csv"; }
    public String getFileExtension() { return "csv"; }
}

// 導出工廠
public class ExportFactory {
    private static final Map<String, DocumentExporter> EXPORTERS = Map.of(
        "excel", new ExcelExporter(),
        "pdf", new PdfExporter(),
        "csv", new CsvExporter()
    );
    
    public static DocumentExporter getExporter(String type) {
        return EXPORTERS.getOrDefault(type.toLowerCase(), EXPORTERS.get("csv"));
    }
}

// 使用:Controller層
@GetMapping("/export")
public void exportData(@RequestParam String type, HttpServletResponse response) throws IOException {
    List<DataRow> data = dataService.queryAll();
    DocumentExporter exporter = ExportFactory.getExporter(type);
    
    response.setContentType(exporter.getContentType());
    response.setHeader("Content-Disposition", "attachment; filename=data." + exporter.getFileExtension());
    
    exporter.export(data, response.getOutputStream());
}

二、結(jié)構(gòu)型模式

3. 代理模式(Proxy)

具體應用場景與實例

應用場景 具體例子 代理類型 解決的問題
日志記錄 Spring AOP的@Log注解 JDK動態(tài)代理 無侵入記錄方法入?yún)⒊鰠?/td>
權(quán)限控制 Shiro、Spring Security的方法鑒權(quán) JDK動態(tài)代理 方法級別的權(quán)限檢查
事務管理 Spring的@Transactional JDK動態(tài)代理/CGLIB 自動開啟/提交/回滾事務
性能監(jiān)控 方法執(zhí)行時間統(tǒng)計 JDK動態(tài)代理 埋點監(jiān)控,不影響業(yè)務代碼
延遲加載 MyBatis的Mapper接口 JDK動態(tài)代理 真正執(zhí)行SQL時才訪問數(shù)據(jù)庫
遠程調(diào)用 Dubbo、Feign客戶端 JDK動態(tài)代理 本地調(diào)用像遠程服務
連接池 Druid的PooledConnection 靜態(tài)代理 連接歸還池而非真正關(guān)閉
緩存 Spring Cache的@Cacheable AOP代理 自動緩存方法返回值

實際代碼示例

// ========== 性能監(jiān)控代理(實際項目常用)==========
@Component
@Aspect  // Spring AOP實現(xiàn)
public class PerformanceMonitorAspect {
    
    @Around("execution(* com.example.service.*.*(..))")
    public Object monitor(ProceedingJoinPoint point) throws Throwable {
        String methodName = point.getSignature().toShortString();
        long start = System.currentTimeMillis();
        
        try {
            return point.proceed();  // 執(zhí)行真實方法
        } finally {
            long cost = System.currentTimeMillis() - start;
            if (cost > 1000) {  // 慢查詢預警
                System.err.printf("【慢方法警告】%s 執(zhí)行耗時: %dms%n", methodName, cost);
            } else {
                System.out.printf("【性能監(jiān)控】%s 執(zhí)行耗時: %dms%n", methodName, cost);
            }
        }
    }
}

// 使用:業(yè)務代碼完全無感知
@Service
public class OrderService {
    @Autowired
    private OrderMapper orderMapper;
    
    // 自動被代理,記錄性能
    public Order getOrder(Long id) {
        return orderMapper.selectById(id);
    }
}
// ========== 遠程調(diào)用代理(模擬Dubbo/Feign)==========
// 用戶服務接口
public interface UserService {
    @POST("/api/users")
    User createUser(@Body User user);
    
    @GET("/api/users/{id}")
    User getUser(@Path("id") Long id);
}

// 動態(tài)代理實現(xiàn)遠程調(diào)用
public class RpcProxy implements InvocationHandler {
    private String baseUrl;
    private OkHttpClient httpClient = new OkHttpClient();
    
    public <T> T create(Class<T> clazz, String baseUrl) {
        this.baseUrl = baseUrl;
        return (T) Proxy.newProxyInstance(
            clazz.getClassLoader(),
            new Class<?>[]{clazz},
            this
        );
    }
    
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 解析注解構(gòu)建HTTP請求
        String path = parsePath(method);
        String url = baseUrl + path;
        
        Request.Builder builder = new Request.Builder().url(url);
        
        // 根據(jù)注解類型選擇GET/POST
        if (method.isAnnotationPresent(GET.class)) {
            builder.get();
        } else if (method.isAnnotationPresent(POST.class)) {
            String json = new Gson().toJson(args[0]);
            builder.post(RequestBody.create(json, JSON));
        }
        
        // 執(zhí)行HTTP請求
        Response response = httpClient.newCall(builder.build()).execute();
        String result = response.body().string();
        
        // 反序列化返回對象
        return new Gson().fromJson(result, method.getReturnType());
    }
}

// 使用:像調(diào)用本地方法一樣調(diào)用遠程服務
public class UserClient {
    public static void main(String[] args) {
        RpcProxy proxy = new RpcProxy();
        UserService userService = proxy.create(UserService.class, "http://user-service:8080");
        
        // 實際發(fā)送HTTP請求到遠程服務
        User user = userService.getUser(1001L);
        System.out.println(user.getName());
    }
}
// ========== 連接池代理(Druid的實現(xiàn)原理)==========
// 真實連接
public class RealConnection implements Connection {
    public void close() {
        // 真正關(guān)閉數(shù)據(jù)庫連接
        System.out.println("真正關(guān)閉數(shù)據(jù)庫連接");
    }
    // ... 其他方法
}

// 連接代理 - 關(guān)鍵:close時不真正關(guān)閉,歸還連接池
public class PooledConnection implements InvocationHandler {
    private Connection realConnection;
    private ConnectionPool pool;
    private Connection proxyConnection;  // 代理對象自己
    
    public PooledConnection(Connection real, ConnectionPool pool) {
        this.realConnection = real;
        this.pool = pool;
        // 創(chuàng)建代理對象
        this.proxyConnection = (Connection) Proxy.newProxyInstance(
            Connection.class.getClassLoader(),
            new Class<?>[]{Connection.class},
            this
        );
    }
    
    public Connection getProxy() {
        return proxyConnection;
    }
    
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        String methodName = method.getName();
        
        // 關(guān)鍵攔截:close方法不真正關(guān)閉,歸還連接池
        if ("close".equals(methodName)) {
            pool.returnConnection(this);  // 歸還池子
            System.out.println("【代理】連接歸還連接池,而非真正關(guān)閉");
            return null;
        }
        
        // 其他方法委托給真實連接
        return method.invoke(realConnection, args);
    }
}

// 使用:開發(fā)者調(diào)用close(),實際歸還連接池
Connection conn = pool.getConnection();
// ... 使用連接
conn.close();  // 實際執(zhí)行PooledConnection的代理邏輯,歸還而非關(guān)閉

4. 裝飾器模式(Decorator)

具體應用場景與實例

應用場景 具體例子 裝飾內(nèi)容 解決的問題
Java IO流 BufferedInputStream包裝FileInputStream 增加緩沖功能 減少系統(tǒng)調(diào)用次數(shù),提高性能
IO字符編碼 InputStreamReader包裝InputStream 增加編碼轉(zhuǎn)換 字節(jié)流→字符流,處理中文編碼
IO按行讀取 BufferedReader包裝Reader 增加行緩沖 支持readLine(),方便文本處理
壓縮流 GZIPOutputStream包裝FileOutputStream 增加壓縮功能 寫文件時自動壓縮
加密流 CipherOutputStream 增加加密功能 寫數(shù)據(jù)時自動加密
集合同步 Collections.synchronizedList() 增加線程安全 普通List變線程安全
集合不可修改 Collections.unmodifiableList() 增加只讀限制 防止外部修改內(nèi)部數(shù)據(jù)
UI組件 Java Swing的JScrollPane包裝JTable 增加滾動條 表格內(nèi)容過多時可滾動
咖啡/奶茶 星巴克訂單系統(tǒng) 配料動態(tài)疊加 基礎(chǔ)飲品+多種配料組合計價

實際代碼示例

// ========== Java IO流的裝飾器鏈(最經(jīng)典例子)==========
public class IODecoratorDemo {
    public static void main(String[] args) throws IOException {
        // 原始組件:文件字節(jié)流
        InputStream fileStream = new FileInputStream("data.txt");
        
        // 第1層裝飾:增加緩沖(減少系統(tǒng)調(diào)用)
        InputStream bufferedStream = new BufferedInputStream(fileStream, 8192);  // 8KB緩沖
        
        // 第2層裝飾:增加壓縮解壓
        InputStream gzipStream = new GZIPInputStream(bufferedStream);
        
        // 第3層裝飾:字節(jié)轉(zhuǎn)字符(指定編碼)
        Reader reader = new InputStreamReader(gzipStream, StandardCharsets.UTF_8);
        
        // 第4層裝飾:按行讀取
        BufferedReader lineReader = new BufferedReader(reader);
        
        // 使用:從壓縮的UTF-8編碼文件中按行讀取
        String line;
        while ((line = lineReader.readLine()) != null) {
            System.out.println(line);
        }
        
        // 關(guān)閉最外層,內(nèi)層自動級聯(lián)關(guān)閉
        lineReader.close();
    }
}
// ========== 權(quán)限裝飾器(數(shù)據(jù)訪問層常用)==========
// 基礎(chǔ)數(shù)據(jù)訪問接口
public interface DataRepository {
    List<Data> query(String condition);
    void save(Data data);
}

// 基礎(chǔ)實現(xiàn)
public class DatabaseRepository implements DataRepository {
    public List<Data> query(String condition) {
        // 執(zhí)行SQL查詢
        return jdbcTemplate.query("SELECT * FROM data WHERE " + condition, ...);
    }
    public void save(Data data) {
        jdbcTemplate.update("INSERT INTO data ...", ...);
    }
}

// 裝飾器基類
public abstract class RepositoryDecorator implements DataRepository {
    protected DataRepository wrapped;
    public RepositoryDecorator(DataRepository repo) { this.wrapped = repo; }
    public List<Data> query(String condition) { return wrapped.query(condition); }
    public void save(Data data) { wrapped.save(data); }
}

// 權(quán)限控制裝飾器
public class SecurityDecorator extends RepositoryDecorator {
    private CurrentUser user;
    
    public SecurityDecorator(DataRepository repo, CurrentUser user) {
        super(repo);
        this.user = user;
    }
    
    @Override
    public List<Data> query(String condition) {
        // 添加數(shù)據(jù)權(quán)限過濾
        String securedCondition = condition + " AND dept_id IN " + user.getAccessibleDepts();
        System.out.println("【權(quán)限裝飾】添加數(shù)據(jù)權(quán)限過濾: " + securedCondition);
        return super.query(securedCondition);
    }
    
    @Override
    public void save(Data data) {
        // 檢查是否有寫入權(quán)限
        if (!user.hasPermission("data:write")) {
            throw new AccessDeniedException("無數(shù)據(jù)寫入權(quán)限");
        }
        super.save(data);
    }
}

// 緩存裝飾器
public class CacheDecorator extends RepositoryDecorator {
    private Cache<String, List<Data>> cache = Caffeine.newBuilder()
        .expireAfterWrite(10, TimeUnit.MINUTES)
        .build();
    
    public CacheDecorator(DataRepository repo) { super(repo); }
    
    @Override
    public List<Data> query(String condition) {
        // 先查緩存
        return cache.get(condition, key -> {
            System.out.println("【緩存裝飾】緩存未命中,查詢數(shù)據(jù)庫: " + key);
            return super.query(key);
        });
    }
}

// 使用:動態(tài)疊加功能
DataRepository repo = new DatabaseRepository();           // 基礎(chǔ)功能
repo = new SecurityDecorator(repo, currentUser);          // 疊加權(quán)限控制
repo = new CacheDecorator(repo);                          // 疊加緩存

// 調(diào)用時自動執(zhí)行權(quán)限檢查→緩存查詢→數(shù)據(jù)庫查詢
List<Data> result = repo.query("status=1");
// ========== 星巴克咖啡訂單系統(tǒng)(經(jīng)典教學案例)==========
// 組件接口
public interface Beverage {
    String getDescription();
    BigDecimal getCost();
}

// 基礎(chǔ)飲品
public class Espresso implements Beverage {
    public String getDescription() { return "濃縮咖啡"; }
    public BigDecimal getCost() { return new BigDecimal("25.00"); }
}

public class DarkRoast implements Beverage {
    public String getDescription() { return "深度烘焙咖啡"; }
    public BigDecimal getCost() { return new BigDecimal("20.00"); }
}

// 裝飾器基類
public abstract class CondimentDecorator implements Beverage {
    protected Beverage beverage;
    public CondimentDecorator(Beverage b) { this.beverage = b; }
    public abstract String getDescription();
}

// 具體配料裝飾器
public class Milk extends CondimentDecorator {
    public Milk(Beverage b) { super(b); }
    public String getDescription() { return beverage.getDescription() + " + 牛奶"; }
    public BigDecimal getCost() { return beverage.getCost().add(new BigDecimal("3.00")); }
}

public class Mocha extends CondimentDecorator {
    public Mocha(Beverage b) { super(b); }
    public String getDescription() { return beverage.getDescription() + " + 摩卡"; }
    public BigDecimal getCost() { return beverage.getCost().add(new BigDecimal("5.00")); }
}

public class Whip extends CondimentDecorator {
    public Whip(Beverage b) { super(b); }
    public String getDescription() { return beverage.getDescription() + " + 奶泡"; }
    public BigDecimal getCost() { return beverage.getCost().add(new BigDecimal("2.50")); }
}

// 使用:自由組合訂單
public class CoffeeShop {
    public static void main(String[] args) {
        // 訂單1:濃縮咖啡 + 雙份摩卡 + 奶泡
        Beverage order1 = new Whip(new Mocha(new Mocha(new Espresso())));
        System.out.println(order1.getDescription() + " = ¥" + order1.getCost());
        // 輸出:濃縮咖啡 + 摩卡 + 摩卡 + 奶泡 = ¥37.50
        
        // 訂單2:深度烘焙 + 牛奶 + 摩卡(大杯加價)
        Beverage order2 = new Mocha(new Milk(new DarkRoast()));
        System.out.println(order2.getDescription() + " = ¥" + order2.getCost());
        // 輸出:深度烘焙咖啡 + 牛奶 + 摩卡 = ¥28.00
    }
}

三、行為型模式

5. 策略模式(Strategy)

具體應用場景與實例

應用場景 具體例子 策略 解決的問題
電商促銷 雙11、618、黑五活動 滿減、折扣、秒殺、拼團 不同活動不同計價規(guī)則
會員等級 普通/銀卡/金卡/鉆石會員 不同折扣率、積分倍數(shù) 會員權(quán)益差異化
排序算法 Collections.sort() 快速排序、歸并排序、堆排序 根據(jù)數(shù)據(jù)規(guī)模選擇最優(yōu)算法
壓縮算法 ZIP、GZIP、LZ4、Snappy 不同壓縮比和速度策略 根據(jù)場景選擇壓縮方案
圖片處理 縮略圖、水印、裁剪 不同處理算法 根據(jù)需求選擇處理方式
路由算法 Dubbo負載均衡 隨機、輪詢、一致性哈希、最少活躍 根據(jù)服務狀態(tài)選擇調(diào)用策略
驗證碼 短信、郵件、圖片、滑塊 不同驗證方式 根據(jù)安全等級選擇驗證策略
文件存儲 本地、OSS、S3、FastDFS 不同存儲后端 根據(jù)文件類型選擇存儲位置

實際代碼示例

// ========== 電商促銷策略系統(tǒng)(實際項目)==========
// 策略接口
public interface PromotionStrategy {
    String getName();
    BigDecimal calculateDiscount(Order order);  // 計算優(yōu)惠金額
    boolean isApplicable(Order order);           // 是否適用該訂單
}

// 具體策略1:滿減促銷
public class FullReductionStrategy implements PromotionStrategy {
    private BigDecimal fullAmount;   // 滿多少
    private BigDecimal reduction;    // 減多少
    
    public FullReductionStrategy(BigDecimal full, BigDecimal reduction) {
        this.fullAmount = full;
        this.reduction = reduction;
    }
    
    public String getName() { return "滿" + fullAmount + "減" + reduction; }
    
    public boolean isApplicable(Order order) {
        return order.getTotalAmount().compareTo(fullAmount) >= 0;
    }
    
    public BigDecimal calculateDiscount(Order order) {
        if (!isApplicable(order)) return BigDecimal.ZERO;
        return reduction;
    }
}

// 具體策略2:折扣促銷
public class DiscountStrategy implements PromotionStrategy {
    private BigDecimal discountRate;  // 0.8 = 8折
    
    public DiscountStrategy(BigDecimal rate) { this.discountRate = rate; }
    
    public String getName() { return discountRate.multiply(new BigDecimal("10")) + "折"; }
    
    public boolean isApplicable(Order order) { return true; }  // 全場通用
    
    public BigDecimal calculateDiscount(Order order) {
        BigDecimal payAmount = order.getTotalAmount().multiply(discountRate);
        return order.getTotalAmount().subtract(payAmount);
    }
}

// 具體策略3:秒殺促銷(限時限量)
public class SeckillStrategy implements PromotionStrategy {
    private LocalDateTime startTime, endTime;
    private int stock;  // 庫存
    
    public boolean isApplicable(Order order) {
        LocalDateTime now = LocalDateTime.now();
        return now.isAfter(startTime) && now.isBefore(endTime) && stock > 0;
    }
    
    public BigDecimal calculateDiscount(Order order) {
        // 秒殺價 = 原價 * 0.5
        return order.getTotalAmount().multiply(new BigDecimal("0.5"));
    }
}

// 具體策略4:拼團促銷
public class GroupBuyStrategy implements PromotionStrategy {
    private int minGroupSize;  // 最低成團人數(shù)
    
    public boolean isApplicable(Order order) {
        return order.getGroupSize() >= minGroupSize;
    }
    
    public BigDecimal calculateDiscount(Order order) {
        // 拼團價根據(jù)人數(shù)階梯降價
        int size = order.getGroupSize();
        if (size >= 10) return order.getTotalAmount().multiply(new BigDecimal("0.3"));  // 7折
        if (size >= 5) return order.getTotalAmount().multiply(new BigDecimal("0.2"));   // 8折
        return order.getTotalAmount().multiply(new BigDecimal("0.1"));                  // 9折
    }
}

// 策略上下文 - 促銷引擎
@Component
public class PromotionEngine {
    private List<PromotionStrategy> strategies = new ArrayList<>();
    
    @Autowired
    public PromotionEngine(List<PromotionStrategy> strategyList) {
        this.strategies = strategyList;  // Spring自動注入所有策略
    }
    
    // 為訂單選擇最優(yōu)促銷策略
    public PromotionResult applyBestPromotion(Order order) {
        PromotionResult bestResult = null;
        BigDecimal maxDiscount = BigDecimal.ZERO;
        
        for (PromotionStrategy strategy : strategies) {
            if (strategy.isApplicable(order)) {
                BigDecimal discount = strategy.calculateDiscount(order);
                if (discount.compareTo(maxDiscount) > 0) {
                    maxDiscount = discount;
                    bestResult = new PromotionResult(strategy.getName(), discount);
                }
            }
        }
        
        return bestResult != null ? bestResult : new PromotionResult("無優(yōu)惠", BigDecimal.ZERO);
    }
    
    // 疊加多個促銷(如優(yōu)惠券+滿減)
    public PromotionResult applyStackedPromotions(Order order, List<String> strategyNames) {
        BigDecimal totalDiscount = BigDecimal.ZERO;
        // ... 疊加計算邏輯
        return new PromotionResult("疊加優(yōu)惠", totalDiscount);
    }
}

// 使用:Controller層
@RestController
public class OrderController {
    @Autowired
    private PromotionEngine promotionEngine;
    @Autowired
    private OrderService orderService;
    
    @PostMapping("/order/preview")
    public OrderPreviewVO previewOrder(@RequestBody OrderDTO dto) {
        Order order = orderService.buildOrder(dto);
        
        // 自動計算最優(yōu)促銷
        PromotionResult promotion = promotionEngine.applyBestPromotion(order);
        
        OrderPreviewVO vo = new OrderPreviewVO();
        vo.setOriginalAmount(order.getTotalAmount());
        vo.setDiscountAmount(promotion.getDiscount());
        vo.setFinalAmount(order.getTotalAmount().subtract(promotion.getDiscount()));
        vo.setPromotionName(promotion.getName());
        
        return vo;
    }
}
// ========== 負載均衡策略(Dubbo/Ribbon實現(xiàn)原理)==========
public interface LoadBalanceStrategy {
    ServiceInstance select(List<ServiceInstance> instances, Invocation invocation);
}

// 隨機策略
public class RandomStrategy implements LoadBalanceStrategy {
    private Random random = new Random();
    
    public ServiceInstance select(List<ServiceInstance> instances, Invocation invocation) {
        int index = random.nextInt(instances.size());
        return instances.get(index);
    }
}

// 輪詢策略
public class RoundRobinStrategy implements LoadBalanceStrategy {
    private AtomicInteger counter = new AtomicInteger(0);
    
    public ServiceInstance select(List<ServiceInstance> instances, Invocation invocation) {
        int index = counter.getAndIncrement() % instances.size();
        return instances.get(index);
    }
}

// 一致性哈希(相同參數(shù)路由到相同節(jié)點)
public class ConsistentHashStrategy implements LoadBalanceStrategy {
    private TreeMap<Long, ServiceInstance> virtualNodes = new TreeMap<>();
    private static final int VIRTUAL_NODE_COUNT = 150;
    
    public ServiceInstance select(List<ServiceInstance> instances, Invocation invocation) {
        // 根據(jù)方法參數(shù)計算hash
        String key = invocation.getMethodName() + invocation.getArguments().toString();
        long hash = hash(key);
        
        // 找到順時針第一個虛擬節(jié)點
        Map.Entry<Long, ServiceInstance> entry = virtualNodes.ceilingEntry(hash);
        if (entry == null) entry = virtualNodes.firstEntry();
        
        return entry.getValue();
    }
}

// 最少活躍調(diào)用數(shù)(自動避讓慢節(jié)點)
public class LeastActiveStrategy implements LoadBalanceStrategy {
    public ServiceInstance select(List<ServiceInstance> instances, Invocation invocation) {
        ServiceInstance best = null;
        int leastActive = Integer.MAX_VALUE;
        
        for (ServiceInstance instance : instances) {
            int active = instance.getActiveCount();  // 當前進行中的調(diào)用數(shù)
            if (active < leastActive) {
                leastActive = active;
                best = instance;
            }
        }
        return best;
    }
}

// 使用:Dubbo的負載均衡配置
@Reference(loadbalance = "leastactive")  // 或 random/roundrobin/consistenthash
private UserService userService;

6. 觀察者模式(Observer)

具體應用場景與實例

應用場景 具體例子 觀察者 解決的問題
Spring事件機制 ApplicationEventPublisher @EventListener 解耦業(yè)務模塊,如訂單創(chuàng)建后發(fā)短信、扣庫存、加積分
消息隊列 Kafka、RabbitMQ的消費者 消費者組 廣播消息給多個處理服務
配置中心 Nacos、Apollo的配置監(jiān)聽 客戶端應用 配置變更實時推送
股票行情 同花順、東方財富APP 自選股列表 價格變動實時更新UI
游戲系統(tǒng) 玩家狀態(tài)變化 好友系統(tǒng)、排行榜、成就系統(tǒng) 一處變化多處響應
工作流引擎 審批流程狀態(tài)變更 郵件通知、短信通知、待辦推送 狀態(tài)變更自動觸發(fā)后續(xù)動作
ZK節(jié)點監(jiān)聽 ZooKeeper的Watcher 客戶端 節(jié)點數(shù)據(jù)變化實時通知
Redisson分布式鎖 鎖釋放通知 等待線程 鎖被釋放時喚醒等待者

實際代碼示例

// ========== Spring事件機制(最常用)==========
// 定義事件
public class OrderCreatedEvent extends ApplicationEvent {
    private Long orderId;
    private Long userId;
    private BigDecimal amount;
    private LocalDateTime createTime;
    
    public OrderCreatedEvent(Object source, Long orderId, Long userId, BigDecimal amount) {
        super(source);
        this.orderId = orderId;
        this.userId = userId;
        this.amount = amount;
        this.createTime = LocalDateTime.now();
    }
    // getters...
}

// 觀察者1:發(fā)送短信通知
@Component
public class SmsNotificationListener {
    @Autowired
    private SmsService smsService;
    
    @EventListener
    public void handleOrderCreated(OrderCreatedEvent event) {
        System.out.println("【短信服務】訂單" + event.getOrderId() + "創(chuàng)建成功,發(fā)送短信給用戶" + event.getUserId());
        smsService.send(event.getUserId(), "您的訂單已創(chuàng)建,金額:" + event.getAmount());
    }
}

// 觀察者2:扣減庫存
@Component
public class InventoryListener {
    @Autowired
    private InventoryService inventoryService;
    
    @EventListener
    @Async  // 異步執(zhí)行,不阻塞主流程
    public void handleOrderCreated(OrderCreatedEvent event) {
        System.out.println("【庫存服務】扣減訂單" + event.getOrderId() + "的庫存");
        inventoryService.decreaseStock(event.getOrderId());
    }
}

// 觀察者3:增加用戶積分
@Component
public class PointsListener {
    @Autowired
    private PointsService pointsService;
    
    @EventListener
    @Async
    @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)  // 事務提交后才執(zhí)行
    public void handleOrderCreated(OrderCreatedEvent event) {
        System.out.println("【積分服務】訂單" + event.getOrderId() + "增加積分");
        int points = event.getAmount().intValue();  // 1元=1積分
        pointsService.addPoints(event.getUserId(), points);
    }
}

// 觀察者4:記錄操作日志
@Component
public class AuditLogListener {
    @EventListener
    public void handleOrderCreated(OrderCreatedEvent event) {
        System.out.println("【審計日志】記錄訂單創(chuàng)建:" + event.getOrderId());
        // 記錄到ES或數(shù)據(jù)庫
    }
}

// 發(fā)布事件(主題)
@Service
public class OrderService {
    @Autowired
    private ApplicationEventPublisher eventPublisher;
    @Autowired
    private OrderRepository orderRepository;
    
    @Transactional
    public Order createOrder(OrderDTO dto) {
        // 1. 保存訂單
        Order order = new Order();
        // ... 設置屬性
        orderRepository.save(order);
        
        // 2. 發(fā)布事件(自動通知所有觀察者)
        OrderCreatedEvent event = new OrderCreatedEvent(this, order.getId(), order.getUserId(), order.getAmount());
        eventPublisher.publishEvent(event);
        
        // 3. 立即返回,后續(xù)處理異步執(zhí)行
        return order;
    }
}
// ========== 配置中心實時推送(模擬Nacos)==========
// 配置主題
public class ConfigCenter {
    private Map<String, String> configs = new ConcurrentHashMap<>();
    private Map<String, List<ConfigListener>> listeners = new ConcurrentHashMap<>();
    
    // 注冊監(jiān)聽器
    public void addListener(String dataId, ConfigListener listener) {
        listeners.computeIfAbsent(dataId, k -> new CopyOnWriteArrayList<>()).add(listener);
    }
    
    // 發(fā)布配置變更
    public void publishConfig(String dataId, String content) {
        configs.put(dataId, content);
        // 通知所有監(jiān)聽該配置的觀察者
        List<ConfigListener> list = listeners.get(dataId);
        if (list != null) {
            for (ConfigListener listener : list) {
                listener.onConfigChange(dataId, content);
            }
        }
    }
}

// 配置監(jiān)聽器接口
public interface ConfigListener {
    void onConfigChange(String dataId, String content);
}

// 具體觀察者1:數(shù)據(jù)庫連接池動態(tài)刷新
public class DataSourceConfigListener implements ConfigListener {
    private HikariDataSource dataSource;
    
    public void onConfigChange(String dataId, String content) {
        if ("db-config".equals(dataId)) {
            System.out.println("【數(shù)據(jù)源】檢測到數(shù)據(jù)庫配置變更,動態(tài)刷新連接池");
            // 解析新配置
            Properties props = parseConfig(content);
            // 動態(tài)調(diào)整連接池參數(shù)(無需重啟)
            dataSource.setMaximumPoolSize(Integer.parseInt(props.getProperty("maxPoolSize")));
            dataSource.setConnectionTimeout(Long.parseLong(props.getProperty("connectionTimeout")));
        }
    }
}

// 具體觀察者2:日志級別動態(tài)調(diào)整
public class LoggerConfigListener implements ConfigListener {
    public void onConfigChange(String dataId, String content) {
        if ("log-config".equals(dataId)) {
            System.out.println("【日志系統(tǒng)】檢測到日志級別變更,動態(tài)調(diào)整");
            Map<String, String> config = parseConfig(content);
            String rootLevel = config.get("root.level");
            LoggerContext ctx = (LoggerContext) LoggerFactory.getILoggerFactory();
            ctx.getLogger("ROOT").setLevel(Level.valueOf(rootLevel));
        }
    }
}

// 具體觀察者3:業(yè)務開關(guān)(熔斷、功能開關(guān))
public class FeatureSwitchListener implements ConfigListener {
    private Map<String, Boolean> featureSwitches = new ConcurrentHashMap<>();
    
    public void onConfigChange(String dataId, String content) {
        if ("feature-switches".equals(dataId)) {
            System.out.println("【業(yè)務開關(guān)】刷新功能開關(guān)配置");
            Map<String, String> switches = parseConfig(content);
            switches.forEach((key, value) -> {
                featureSwitches.put(key, Boolean.parseBoolean(value));
                System.out.println("  - " + key + ": " + value);
            });
        }
    }
    
    public boolean isFeatureEnabled(String feature) {
        return featureSwitches.getOrDefault(feature, false);
    }
}

// 使用
public class ConfigDemo {
    public static void main(String[] args) {
        ConfigCenter configCenter = new ConfigCenter();
        
        // 應用啟動時注冊監(jiān)聽器
        configCenter.addListener("db-config", new DataSourceConfigListener());
        configCenter.addListener("log-config", new LoggerConfigListener());
        configCenter.addListener("feature-switches", new FeatureSwitchListener());
        
        // 模擬配置中心推送變更
        configCenter.publishConfig("db-config", "maxPoolSize=50\nconnectionTimeout=30000");
        configCenter.publishConfig("feature-switches", "newPaymentGateway=true\nblackFridayMode=false");
    }
}

7. 模板方法模式(Template Method)

具體應用場景與實例

應用場景 具體例子 固定骨架 可變步驟
數(shù)據(jù)導入 Excel/CSV/JSON導入 校驗→解析→清洗→保存→日志 解析邏輯不同
報表導出 PDF/Excel/Word導出 查詢數(shù)據(jù)→填充模板→生成文件→上傳OSS 模板填充方式不同
支付流程 支付寶/微信/銀聯(lián)支付 創(chuàng)建訂單→調(diào)用渠道→處理回調(diào)→更新狀態(tài) 調(diào)用渠道API不同
審批流程 請假/報銷/入職審批 提交申請→逐級審批→通知結(jié)果→歸檔 審批規(guī)則不同
JDBC操作 Spring的JdbcTemplate 獲取連接→執(zhí)行SQL→處理結(jié)果→釋放資源 SQL和結(jié)果處理不同
Servlet處理 HttpServlet的doGet/doPost 接收請求→解析參數(shù)→業(yè)務處理→返回響應 業(yè)務邏輯不同
單元測試 JUnit的setUptesttearDown 初始化→執(zhí)行測試→清理資源 測試用例不同
游戲AI 不同難度級別的NPC 感知→決策→行動 決策算法不同

實際代碼示例

// ========== 數(shù)據(jù)導入模板(實際項目常用)==========
public abstract class DataImporter<T> {
    
    // ========== 模板方法:final防止子類篡改流程 ==========
    public final ImportResult importData(MultipartFile file, Long operatorId) {
        List<T> records = new ArrayList<>();
        List<String> errors = new ArrayList<>();
        
        try {
            // 1. 前置校驗(固定)
            validateFile(file);
            
            // 2. 數(shù)據(jù)解析(抽象,子類實現(xiàn))
            records = parse(file.getInputStream());
            
            // 3. 數(shù)據(jù)清洗(鉤子,可選重寫)
            clean(records);
            
            // 4. 業(yè)務校驗(抽象,子類實現(xiàn))
            validateBusiness(records, errors);
            if (!errors.isEmpty()) {
                return ImportResult.fail(errors);
            }
            
            // 5. 數(shù)據(jù)轉(zhuǎn)換(抽象)
            List<Entity> entities = convert(records);
            
            // 6. 批量保存(固定)
            saveBatch(entities, operatorId);
            
            // 7. 后置處理(鉤子)
            afterImport(entities, operatorId);
            
            // 8. 記錄日志(固定)
            logImport(file.getOriginalFilename(), records.size(), operatorId);
            
            return ImportResult.success(records.size());
            
        } catch (Exception e) {
            return ImportResult.fail("導入異常:" + e.getMessage());
        }
    }
    
    // ========== 固定步驟 ==========
    private void validateFile(MultipartFile file) {
        if (file.isEmpty()) throw new IllegalArgumentException("文件不能為空");
        String filename = file.getOriginalFilename();
        if (!filename.matches(getFilePattern())) {
            throw new IllegalArgumentException("不支持的文件格式");
        }
    }
    
    private void saveBatch(List<Entity> entities, Long operatorId) {
        // 使用MyBatis-Plus批量插入
        service.saveBatch(entities, 500);
    }
    
    private void logImport(String filename, int count, Long operatorId) {
        ImportLog log = new ImportLog();
        log.setFileName(filename);
        log.setRecordCount(count);
        log.setOperatorId(operatorId);
        log.setImportTime(LocalDateTime.now());
        logService.save(log);
    }
    
    // ========== 抽象步驟:子類必須實現(xiàn) ==========
    protected abstract String getFilePattern();           // 文件格式正則
    protected abstract List<T> parse(InputStream is);     // 解析文件
    protected abstract void validateBusiness(List<T> records, List<String> errors); // 業(yè)務校驗
    protected abstract List<Entity> convert(List<T> records); // 轉(zhuǎn)換為實體
    
    // ========== 鉤子方法:子類可選重寫 ==========
    protected void clean(List<T> records) {
        // 默認空實現(xiàn):去除空行、trim等基礎(chǔ)清洗
        records.removeIf(Objects::isNull);
    }
    
    protected void afterImport(List<Entity> entities, Long operatorId) {
        // 默認空實現(xiàn)
    }
}

// ========== 具體實現(xiàn)1:員工Excel導入 ==========
@Component
public class EmployeeExcelImporter extends DataImporter<EmployeeRow> {
    
    @Override
    protected String getFilePattern() {
        return ".*\\.xlsx?$";
    }
    
    @Override
    protected List<EmployeeRow> parse(InputStream is) {
        // 使用EasyExcel解析
        return EasyExcel.read(is, EmployeeRow.class, new PageReadListener<>()).sheet().doReadSync();
    }
    
    @Override
    protected void validateBusiness(List<EmployeeRow> records, List<String> errors) {
        Set<String> idCards = new HashSet<>();
        for (int i = 0; i < records.size(); i++) {
            EmployeeRow row = records.get(i);
            // 校驗必填
            if (StringUtils.isBlank(row.getName())) {
                errors.add("第" + (i+1) + "行:姓名不能為空");
            }
            // 校驗身份證唯一性
            if (!idCards.add(row.getIdCard())) {
                errors.add("第" + (i+1) + "行:身份證" + row.getIdCard() + "重復");
            }
            // 校驗部門是否存在
            if (!deptService.exists(row.getDeptCode())) {
                errors.add("第" + (i+1) + "行:部門" + row.getDeptCode() + "不存在");
            }
        }
    }
    
    @Override
    protected List<Entity> convert(List<EmployeeRow> records) {
        return records.stream().map(row -> {
            Employee emp = new Employee();
            emp.setName(row.getName());
            emp.setIdCard(row.getIdCard());
            emp.setDeptId(deptService.getByCode(row.getDeptCode()).getId());
            emp.setEntryDate(parseDate(row.getEntryDate()));
            return emp;
        }).collect(Collectors.toList());
    }
    
    @Override
    protected void afterImport(List<Entity> entities, Long operatorId) {
        // 發(fā)送入職歡迎郵件
        for (Entity emp : entities) {
            mailService.sendWelcomeEmail(((Employee)emp).getEmail());
        }
    }
}

// ========== 具體實現(xiàn)2:商品CSV導入 ==========
@Component
public class ProductCsvImporter extends DataImporter<ProductRow> {
    
    @Override
    protected String getFilePattern() {
        return ".*\\.csv$";
    }
    
    @Override
    protected List<ProductRow> parse(InputStream is) {
        // 使用Apache Commons CSV解析
        List<ProductRow> rows = new ArrayList<>();
        Reader reader = new InputStreamReader(is, StandardCharsets.UTF_8);
        CSVFormat format = CSVFormat.DEFAULT.withFirstRecordAsHeader();
        
        for (CSVRecord record : format.parse(reader)) {
            ProductRow row = new ProductRow();
            row.setSku(record.get("SKU"));
            row.setName(record.get("商品名稱"));
            row.setPrice(new BigDecimal(record.get("價格")));
            rows.add(row);
        }
        return rows;
    }
    
    @Override
    protected void clean(List<ProductRow> records) {
        super.clean(records);
        // 額外清洗:去除SKU前后空格,統(tǒng)一大寫
        for (ProductRow row : records) {
            row.setSku(row.getSku().trim().toUpperCase());
        }
    }
    
    @Override
    protected void validateBusiness(List<ProductRow> records, List<String> errors) {
        // 校驗SKU唯一性、價格范圍等
    }
    
    @Override
    protected List<Entity> convert(List<ProductRow> records) {
        // 轉(zhuǎn)換為Product實體
    }
}

// ========== 具體實現(xiàn)3:訂單JSON導入(API對接)==========
@Component
public class OrderJsonImporter extends DataImporter<OrderRow> {
    
    @Override
    protected String getFilePattern() {
        return ".*\\.json$";
    }
    
    @Override
    protected List<OrderRow> parse(InputStream is) {
        return new Gson().fromJson(new InputStreamReader(is), new TypeToken<List<OrderRow>>(){}.getType());
    }
    
    @Override
    protected void afterImport(List<Entity> entities, Long operatorId) {
        // 同步到ERP系統(tǒng)
        for (Entity order : entities) {
            erpService.syncOrder((Order)order);
        }
    }
    
    // ... 其他實現(xiàn)
}

// 使用:Controller層統(tǒng)一入口
@RestController
public class ImportController {
    @Autowired
    private Map<String, DataImporter> importers;  // Spring自動注入所有Importer
    
    @PostMapping("/import/{type}")
    public ImportResult importData(
            @PathVariable String type,
            @RequestParam MultipartFile file,
            @RequestAttribute Long userId) {
        
        DataImporter importer = importers.get(type + "Importer");
        if (importer == null) {
            throw new IllegalArgumentException("不支持的導入類型:" + type);
        }
        
        return importer.importData(file, userId);
    }
}

// 請求:POST /import/employee  上傳employee.xlsx
// 請求:POST /import/product   上傳product.csv
// 請求:POST /import/order    上傳order.json
// ========== 支付渠道模板(抽象支付流程)==========
public abstract class PaymentChannel {
    
    // 模板方法:定義支付流程骨架
    public final PayResult pay(Order order) {
        // 1. 創(chuàng)建支付流水(固定)
        PaymentRecord record = createPaymentRecord(order);
        
        // 2. 調(diào)用渠道API(抽象,子類實現(xiàn))
        ChannelResponse response = callChannelAPI(order, record);
        
        // 3. 處理響應(固定流程,部分細節(jié)抽象)
        if (response.isSuccess()) {
            record.setStatus("SUCCESS");
            record.setChannelTradeNo(response.getTradeNo());
            updateOrderStatus(order.getId(), "PAID");
            
            // 鉤子:支付成功后的擴展
            onPaySuccess(order, record);
        } else {
            record.setStatus("FAILED");
            record.setErrorMsg(response.getErrorMsg());
            
            // 鉤子:支付失敗后的擴展
            onPayFail(order, record, response);
        }
        
        // 4. 保存記錄并返回(固定)
        paymentRecordRepository.save(record);
        return buildResult(record);
    }
    
    // 抽象步驟:調(diào)用具體渠道API
    protected abstract ChannelResponse callChannelAPI(Order order, PaymentRecord record);
    
    // 鉤子方法:支付成功后的擴展點
    protected void onPaySuccess(Order order, PaymentRecord record) {
        // 默認:發(fā)送支付成功通知
        notificationService.sendPaySuccessNotification(order.getUserId());
    }
    
    // 鉤子方法:支付失敗后的擴展點
    protected void onPayFail(Order order, PaymentRecord record, ChannelResponse response) {
        // 默認:記錄失敗日志
        log.error("支付失敗,訂單:{},原因:{}", order.getId(), response.getErrorMsg());
    }
    
    // 固定步驟...
    private PaymentRecord createPaymentRecord(Order order) { /* ... */ }
    private void updateOrderStatus(Long orderId, String status) { /* ... */ }
    private PayResult buildResult(PaymentRecord record) { /* ... */ }
}

// 支付寶實現(xiàn)
@Component
public class AlipayChannel extends PaymentChannel {
    @Autowired
    private AlipayClient alipayClient;
    
    @Override
    protected ChannelResponse callChannelAPI(Order order, PaymentRecord record) {
        AlipayTradePagePayRequest request = new AlipayTradePagePayRequest();
        request.setBizContent(buildBizContent(order, record));
        
        try {
            AlipayTradePagePayResponse response = alipayClient.pageExecute(request);
            return new ChannelResponse(response.isSuccess(), response.getTradeNo(), null);
        } catch (AlipayApiException e) {
            return new ChannelResponse(false, null, e.getMessage());
        }
    }
    
    @Override
    protected void onPaySuccess(Order order, PaymentRecord record) {
        super.onPaySuccess(order, record);
        // 額外:支付寶積分發(fā)放
        alipayPointService.grantPoints(order.getUserId(), calculatePoints(order.getAmount()));
    }
}

// 微信支付實現(xiàn)
@Component
public class WechatPayChannel extends PaymentChannel {
    @Autowired
    private WXPay wxPay;
    
    @Override
    protected ChannelResponse callChannelAPI(Order order, PaymentRecord record) {
        Map<String, String> params = new HashMap<>();
        params.put("body", order.getProductName());
        params.put("out_trade_no", record.getTradeNo());
        params.put("total_fee", order.getAmount().multiply(new BigDecimal("100")).intValue() + ""); // 分
        params.put("spbill_create_ip", order.getClientIp());
        params.put("notify_url", "https://example.com/notify/wechat");
        params.put("trade_type", "NATIVE");  // 掃碼支付
        
        try {
            Map<String, String> resp = wxPay.unifiedOrder(params);
            boolean success = "SUCCESS".equals(resp.get("return_code")) && "SUCCESS".equals(resp.get("result_code"));
            return new ChannelResponse(success, resp.get("prepay_id"), resp.get("err_code_des"));
        } catch (Exception e) {
            return new ChannelResponse(false, null, e.getMessage());
        }
    }
    
    @Override
    protected void onPayFail(Order order, PaymentRecord record, ChannelResponse response) {
        super.onPayFail(order, record, response);
        // 額外:微信特殊錯誤碼處理
        if ("NOTENOUGH".equals(response.getErrorCode())) {
            // 余額不足,提示用戶換卡
            notificationService.sendInsufficientBalanceReminder(order.getUserId());
        }
    }
}

四、7種模式應用場景速查表

模式 典型應用場景 具體實例 解決的核心問題
單例 全局唯一資源管理 數(shù)據(jù)庫連接池、線程池、配置中心、Spring Bean 控制資源訪問,避免重復創(chuàng)建
工廠 多種產(chǎn)品族的創(chuàng)建 支付渠道、文檔導出、日志框架、負載均衡策略 解耦創(chuàng)建與使用,支持熱插拔
代理 無侵入增強與控制 Spring AOP、MyBatis延遲加載、Dubbo遠程調(diào)用、連接池 控制訪問、增強功能、保護目標
裝飾器 動態(tài)功能疊加 Java IO流、權(quán)限控制、緩存、咖啡配料 比繼承更靈活的功能組合
策略 算法 interchangeable 電商促銷、會員等級、排序算法、負載均衡 消除if-else,運行時切換算法
觀察者 一對多狀態(tài)通知 Spring事件、配置中心、消息隊列、股票行情 解耦發(fā)布訂閱,自動廣播通知
模板方法 固定流程+可變細節(jié) 數(shù)據(jù)導入、支付流程、報表導出、審批流程 復用骨架代碼,開放擴展點

五、實際項目中的組合使用

場景:電商訂單支付流程

訂單創(chuàng)建
    ├── 發(fā)布 OrderCreatedEvent(觀察者模式)
    │       ├── 短信服務發(fā)送通知
    │       ├── 庫存服務扣減庫存
    │       └── 積分服務增加積分
    │
    └── 用戶選擇支付方式
            │
            ├── 支付工廠創(chuàng)建支付渠道(工廠模式)
            │       ├── 支付寶
            │       ├── 微信支付
            │       └── 銀聯(lián)支付
            │
            └── 執(zhí)行支付(模板方法模式)
                    ├── 固定:創(chuàng)建流水→調(diào)用渠道→更新狀態(tài)
                    ├── 抽象:調(diào)用支付寶/微信/銀聯(lián)API(策略模式)
                    └── 鉤子:支付成功后的積分發(fā)放/優(yōu)惠券贈送
                    
支付結(jié)果處理
    ├── 異步通知接收(觀察者模式)
    └── 日志記錄(代理模式增強)

這份文檔涵蓋了7種設計模式在實際項目中的40+個具體應用場景,每個場景都配有可直接使用的代碼示例。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

友情鏈接更多精彩內(nèi)容