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)建 |
BeanFactory、FactoryBean
|
容器統(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的setUp→test→tearDown
|
初始化→執(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+個具體應用場景,每個場景都配有可直接使用的代碼示例。