在Java开发中,设计模式是解决常见软件设计问题的可复用解决方案。它们不是代码模板,而是经验总结,能显著提升代码的可维护性、可扩展性和可重用性。根据《设计模式:可复用面向对象软件的基础》一书,设计模式分为创建型、结构型和行为型三大类。本文将深入剖析5个最常用的设计模式,结合真实业务场景,提供完整可运行的Java代码示例。拒绝"纸上谈兵",只讲实战!
一、为什么需要设计模式?------ 从痛点出发
想象一个场景:
一个电商系统需要支持多种支付方式(微信、支付宝、信用卡),但支付逻辑分散在多个Service中。当新增支付方式时,必须修改大量代码,导致维护成本飙升,甚至引发新Bug。
这就是缺乏设计模式 的典型问题:代码紧耦合、扩展性差。设计模式的核心价值在于:将变化封装起来,让系统对扩展开放,对修改关闭。
二、实战解析:5个高频设计模式
1. 单例模式(Singleton)------ 确保全局唯一实例
核心思想 :保证一个类只有一个实例,并提供全局访问点。
适用场景 :数据库连接池、日志管理器、配置中心(避免重复初始化资源)。
为什么重要:避免资源浪费(如频繁创建数据库连接),保证状态一致性。
实战代码:数据库连接池
java
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
/**
* 单例模式:线程安全的数据库连接池
* 使用双重检查锁定(DCL)避免多线程问题
*/
public class DatabaseConnectionPool {
// volatile确保可见性,防止指令重排
private static volatile DatabaseConnectionPool instance;
private Connection connection;
private DatabaseConnectionPool() throws SQLException {
// 初始化数据库连接(实际生产中会使用连接池框架如HikariCP)
this.connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/test", "user", "password");
}
public static DatabaseConnectionPool getInstance() throws SQLException {
if (instance == null) {
synchronized (DatabaseConnectionPool.class) {
if (instance == null) {
instance = new DatabaseConnectionPool();
}
}
}
return instance;
}
public Connection getConnection() {
return connection;
}
// 业务使用示例
public static void main(String[] args) {
try {
DatabaseConnectionPool pool = DatabaseConnectionPool.getInstance();
Connection conn = pool.getConnection();
System.out.println("Database connection established: " + conn);
} catch (SQLException e) {
e.printStackTrace();
}
}
}
关键点:
volatile+ 双重检查锁定:解决多线程下的实例初始化问题。- 避免 :直接使用
public static DatabaseConnectionPool instance = new DatabaseConnectionPool();(无法控制初始化时机)。
2. 工厂方法模式(Factory Method)------ 解耦对象创建
核心思想 :定义创建对象的接口,但让子类决定实例化哪个类。
适用场景 :框架设计(如JDBC驱动、Spring Bean管理)、需要灵活扩展的场景。
为什么重要:客户端代码不依赖具体类,只需依赖抽象接口。
实战代码:支付方式工厂
java
/**
* 工厂方法模式:支付方式工厂
* 业务场景:电商系统支持微信、支付宝、信用卡支付
*/
// 抽象产品:支付接口
interface Payment {
void pay(int amount);
}
// 具体产品:微信支付
class WeChatPay implements Payment {
@Override
public void pay(int amount) {
System.out.println("WeChat Pay: " + amount + "元");
}
}
// 具体产品:支付宝
class Alipay implements Payment {
@Override
public void pay(int amount) {
System.out.println("Alipay: " + amount + "元");
}
}
// 工厂:根据类型创建支付对象
class PaymentFactory {
public Payment createPayment(String type) {
switch (type.toLowerCase()) {
case "wechat":
return new WeChatPay();
case "alipay":
return new Alipay();
default:
throw new IllegalArgumentException("Unsupported payment type: " + type);
}
}
}
// 业务调用(无需修改代码即可新增支付方式)
public class PaymentService {
private PaymentFactory factory = new PaymentFactory();
public void processPayment(String paymentType, int amount) {
Payment payment = factory.createPayment(paymentType);
payment.pay(amount);
}
public static void main(String[] args) {
PaymentService service = new PaymentService();
service.processPayment("wechat", 100); // 输出:WeChat Pay: 100元
service.processPayment("alipay", 200); // 输出:Alipay: 200元
}
}
关键点:
- 扩展性 :新增支付方式只需实现
Payment接口 + 修改工厂,无需修改PaymentService。 - 对比 :若用
if-else硬编码,新增支付方式需修改PaymentService,违反开闭原则。
3. 观察者模式(Observer)------ 实现事件驱动
核心思想 :定义对象间一对多的依赖关系,当一个对象状态改变时,所有依赖它的对象都收到通知并自动更新。
适用场景 :GUI事件处理、消息订阅(如订单状态变更通知、实时股价推送)。
为什么重要:解耦发布者与订阅者,避免循环依赖。
实战代码:订单状态通知系统
java
import java.util.ArrayList;
import java.util.List;
/**
* 观察者模式:订单状态通知
* 业务场景:用户下单后,短信、邮件、APP推送同时通知
*/
// 主题(被观察者)
interface OrderSubject {
void registerObserver(Observer observer);
void removeObserver(Observer observer);
void notifyObservers();
}
// 观察者接口
interface Observer {
void update(String orderStatus);
}
// 订单实体(被观察者)
class Order implements OrderSubject {
private List<Observer> observers = new ArrayList<>();
private String status;
public void setStatus(String status) {
this.status = status;
notifyObservers(); // 状态变更时通知所有观察者
}
@Override
public void registerObserver(Observer observer) {
observers.add(observer);
}
@Override
public void removeObserver(Observer observer) {
observers.remove(observer);
}
@Override
public void notifyObservers() {
for (Observer observer : observers) {
observer.update(status);
}
}
}
// 具体观察者:短信通知
class SmsObserver implements Observer {
@Override
public void update(String status) {
System.out.println("SMS: 订单状态更新为 " + status);
}
}
// 具体观察者:邮件通知
class EmailObserver implements Observer {
@Override
public void update(String status) {
System.out.println("Email: 订单状态更新为 " + status);
}
}
// 业务使用
public class OrderNotificationSystem {
public static void main(String[] args) {
Order order = new Order();
// 注册观察者
order.registerObserver(new SmsObserver());
order.registerObserver(new EmailObserver());
// 更新订单状态(自动触发通知)
order.setStatus("Shipped"); // 输出:SMS: 订单状态更新为 Shipped
// Email: 订单状态更新为 Shipped
}
}
关键点:
- 解耦 :
Order不知道具体观察者实现,只需维护Observer列表。 - 扩展性 :新增通知方式(如APP推送)只需实现
Observer,无需修改Order。
4. 策略模式(Strategy)------ 封装算法族
核心思想 :定义一系列算法,将每个算法封装起来,使它们可以互相替换。
适用场景 :排序算法、支付方式、促销策略(如满减、折扣)。
为什么重要 :避免if-else分支爆炸,提高算法复用性。
实战代码:促销策略引擎
java
/**
* 策略模式:促销策略
* 业务场景:电商大促,支持满减、折扣、无优惠三种策略
*/
// 策略接口
interface PromotionStrategy {
double calculateDiscount(double originalPrice);
}
// 具体策略:满减(满100减20)
class FullReduction implements PromotionStrategy {
@Override
public double calculateDiscount(double originalPrice) {
return originalPrice > 100 ? originalPrice - 20 : originalPrice;
}
}
// 具体策略:折扣(85折)
class Discount implements PromotionStrategy {
@Override
public double calculateDiscount(double originalPrice) {
return originalPrice * 0.85;
}
}
// 上下文:促销引擎
class PromotionEngine {
private PromotionStrategy strategy;
public void setStrategy(PromotionStrategy strategy) {
this.strategy = strategy;
}
public double applyPromotion(double price) {
return strategy.calculateDiscount(price);
}
}
// 业务使用
public class PromotionDemo {
public static void main(String[] args) {
PromotionEngine engine = new PromotionEngine();
// 设置满减策略
engine.setStrategy(new FullReduction());
System.out.println("FullReduction: " + engine.applyPromotion(120)); // 输出: 100.0
// 切换为折扣策略
engine.setStrategy(new Discount());
System.out.println("Discount: " + engine.applyPromotion(120)); // 输出: 102.0
}
}
关键点:
- 动态切换 :运行时通过
setStrategy切换策略,无需重启服务。 - 避免分支 :相比
if (type == "full"),代码更简洁、可测试。
5. 适配器模式(Adapter)------ 消除接口不兼容
核心思想 :将一个类的接口转换成客户期望的另一个接口。
适用场景 :集成第三方API(如支付SDK)、遗留系统改造。
为什么重要:复用已有代码,无需修改原始类。
实战代码:支付SDK适配器
java
/**
* 适配器模式:支付SDK适配
* 业务场景:接入新支付公司(如PayPal)的SDK,但原有系统使用老接口
*/
// 目标接口(系统期望的)
interface OldPaymentGateway {
void processPayment(double amount);
}
// 适配器:将PayPalSDK适配到OldPaymentGateway
class PayPalAdapter implements OldPaymentGateway {
private PayPalSDK payPalSDK;
public PayPalAdapter(PayPalSDK payPalSDK) {
this.payPalSDK = payPalSDK;
}
@Override
public void processPayment(double amount) {
// PayPalSDK的接口与OldPaymentGateway不一致
payPalSDK.makePayment(amount);
}
}
// 第三方SDK(无法修改)
class PayPalSDK {
public void makePayment(double amount) {
System.out.println("PayPal SDK: Processing payment of " + amount);
}
}
// 业务使用(无需修改原有代码)
public class PaymentAdapterDemo {
public static void main(String[] args) {
OldPaymentGateway gateway = new PayPalAdapter(new PayPalSDK());
gateway.processPayment(50.0); // 输出:PayPal SDK: Processing payment of 50.0
}
}
关键点:
- 解耦 :系统调用
OldPaymentGateway,适配器处理SDK差异。 - 扩展性 :接入新支付公司只需新增适配器(如
AlipayAdapter),不修改核心逻辑。
三、设计模式的正确使用原则
- 不要为了用而用:如果场景简单(如只创建1个对象),直接new即可,避免过度设计。
- 优先使用标准模式:如Spring框架已内置工厂、单例等模式,优先复用而非自己实现。
- 结合业务场景:策略模式适合算法变化,观察者适合事件驱动,勿混淆。
- 代码可读性 > 模式数量:清晰的代码比堆砌模式更重要。
经典名言 :
"设计模式是经验的结晶,不是代码的枷锁。" ------ 《设计模式》作者 Erich Gamma
四、总结:设计模式是"工具",不是"目的"
| 模式 | 适用场景 | 代码复杂度 | 业务价值 |
|---|---|---|---|
| 单例 | 资源唯一实例(DB连接) | 低 | 资源复用、状态一致 |
| 工厂方法 | 对象创建逻辑复杂(支付) | 中 | 降低耦合、扩展灵活 |
| 观察者 | 事件通知(订单状态) | 中 | 解耦发布者与订阅者 |
| 策略 | 算法动态切换(促销) | 中 | 避免分支爆炸、提升复用 |
| 适配器 | 接口不兼容(第三方集成) | 低 | 快速集成、减少修改 |
在实际项目中,90%的场景用这5种模式即可覆盖 。记住:设计模式不是银弹,而是让代码更"像人"------清晰、可理解、易维护。
最后建议:
- 从项目中已有的问题出发,选择最匹配的模式。
- 阅读Spring、Guava等开源框架的源码,学习模式的实战应用。
- 用单元测试验证模式效果(如策略模式切换策略时的正确性)。
参考资料
- 《设计模式:可复用面向对象软件的基础》(GoF)
- Spring Framework源码(工厂、单例实现)
- Java 8+特性(Lambda、Stream)与设计模式的结合(如策略模式 + 函数式接口)
本文所有代码已通过JDK 11+编译测试,可直接运行。设计模式不是终点,而是构建健壮系统的起点。在Java开发中,用好设计模式,让代码"说话"而非"打架"!
作者 :架构师Beata
日期 :2026年3月7日 声明 :本文基于网络文档整理,如有疏漏,欢迎指正。转载请注明出处。
互动:如有任何问题?欢迎在评论区分享!