Java 设计模式实战——6种常用设计模式从理论到代码
·
设计模式是 Java 程序员从「会写代码」迈向「会设计代码」的必经之路。面试常考、源码常见,但不用一口气学 23 种,先把最常用的 6 种搞懂,工作中够用。
一、单例模式——全局只用一个对象
保证一个类在整个应用中只有一个实例,数据库连接池、配置管理器等场景常用。
饿汉式(线程安全,推荐简单场景)
public class Singleton {
// 类加载时直接创建,天然线程安全
private static final Singleton INSTANCE = new Singleton();
private Singleton() {} // 私有构造
public static Singleton getInstance() {
return INSTANCE;
}
}
优点: 写法简单、线程安全。缺点: 不管用不用都创建。
懒汉式(双重检测,推荐标准写法)
public class Singleton {
private static volatile Singleton instance;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) { // 第一次检查
synchronized (Singleton.class) {
if (instance == null) { // 第二次检查(防止多线程)
instance = new Singleton();
}
}
}
return instance;
}
}
volatile 关键词不能省,防止指令重排导致拿到未初始化完成的对象。
静态内部类(最优雅)
public class Singleton {
private Singleton() {}
private static class Holder {
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return Holder.INSTANCE; // 调用时才加载内部类,实现懒加载
}
}
面试推荐用这个,既懒加载又线程安全,还不用写 synchronized。
枚举单例(防反射/防序列化)
public enum SingletonEnum {
INSTANCE;
public void doSomething() {
System.out.println("枚举单例执行");
}
}
// 调用:SingletonEnum.INSTANCE.doSomething();
唯一能防止反射攻击和反序列化破坏的单例方式。
二、工厂模式——把创建对象交给工厂
简单工厂
// 产品接口
interface Product {
void work();
}
class AProduct implements Product {
public void work() { System.out.println("产品A工作"); }
}
class BProduct implements Product {
public void work() { System.out.println("产品B工作"); }
}
// 工厂类:根据参数返回对应产品
class SimpleFactory {
public static Product create(String type) {
switch (type) {
case "A": return new AProduct();
case "B": return new BProduct();
default: throw new IllegalArgumentException("未知类型");
}
}
}
// 使用
Product p = SimpleFactory.create("A");
p.work();
工厂方法(每个产品一个工厂)
interface Factory {
Product create();
}
class AFactory implements Factory {
public Product create() { return new AProduct(); }
}
class BFactory implements Factory {
public Product create() { return new BProduct(); }
}
// 使用
Factory factory = new AFactory();
Product p = factory.create();
简单工厂 vs 工厂方法: 后者更符合开闭原则,新增产品不修改旧代码,加个新工厂类就行。
三、策略模式——把算法封装起来
业务中最常用的设计模式之一,代替 if-else 的利器。
// 策略接口
interface PaymentStrategy {
void pay(BigDecimal amount);
}
// 具体策略
class AlipayStrategy implements PaymentStrategy {
public void pay(BigDecimal amount) {
System.out.println("支付宝支付: " + amount + "元");
}
}
class WechatStrategy implements PaymentStrategy {
public void pay(BigDecimal amount) {
System.out.println("微信支付: " + amount + "元");
}
}
class CardStrategy implements PaymentStrategy {
public void pay(BigDecimal amount) {
System.out.println("银行卡支付: " + amount + "元");
}
}
// 上下文(使用策略的对象)
class PaymentContext {
private PaymentStrategy strategy;
public PaymentContext(PaymentStrategy strategy) {
this.strategy = strategy;
}
public void executePay(BigDecimal amount) {
strategy.pay(amount); // 调用策略
}
}
// 使用
PaymentContext ctx = new PaymentContext(new AlipayStrategy());
ctx.executePay(new BigDecimal("99.9"));
对比 if-else: 新增支付方式只需要加新类,不需要改现有代码。
四、代理模式——给对象加一层控制
Spring AOP、MyBatis 的 Mapper 接口、延迟加载等底层都用到了代理。
静态代理
// 接口
interface UserService {
void saveUser(String name);
}
// 目标类
class UserServiceImpl implements UserService {
public void saveUser(String name) {
System.out.println("保存用户: " + name);
}
}
// 代理类(加日志)
class UserServiceProxy implements UserService {
private UserService target;
public UserServiceProxy(UserService target) {
this.target = target;
}
public void saveUser(String name) {
System.out.println("[日志] saveUser 开始,参数: " + name);
target.saveUser(name);
System.out.println("[日志] saveUser 结束");
}
}
// 使用
UserService service = new UserServiceProxy(new UserServiceImpl());
service.saveUser("张三");
JDK 动态代理(最常用)
UserService target = new UserServiceImpl();
UserService proxy = (UserService) Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
(proxyObj, method, args) -> {
System.out.println("[代理] " + method.getName() + " 开始");
Object result = method.invoke(target, args);
System.out.println("[代理] " + method.getName() + " 结束");
return result;
}
);
proxy.saveUser("张三");
Spring AOP 用法
@Aspect
@Component
public class LogAspect {
@Around("execution(* com.zhang.service.*.*(..))")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("[AOP] 前置通知");
Object result = pjp.proceed(); // 执行目标方法
System.out.println("[AOP] 后置通知");
return result;
}
}
AOP 底层就是动态代理——有接口用 JDK 代理,没有接口用 CGLIB 代理。
五、模板方法模式——固定流程,开放步骤
适合「流程固定、步骤可变」的场景。
// 抽象类:定义流程骨架
abstract class AbstractDataImporter {
// 模板方法(final 防止子类修改流程)
public final void importData() {
openFile();
parseData();
validateData();
saveData();
closeFile();
}
// 固定步骤(子类不需要改)
private void openFile() { System.out.println("打开文件"); }
private void closeFile() { System.out.println("关闭文件"); }
// 可变步骤(子类实现)
protected abstract void parseData();
protected abstract void validateData();
protected abstract void saveData();
}
// 具体实现
class ExcelImporter extends AbstractDataImporter {
protected void parseData() { System.out.println("解析 Excel 数据"); }
protected void validateData() { System.out.println("校验 Excel 数据格式"); }
protected void saveData() { System.out.println("保存 Excel 数据到数据库"); }
}
class CsvImporter extends AbstractDataImporter {
protected void parseData() { System.out.println("解析 CSV 数据"); }
protected void validateData() { System.out.println("校验 CSV 数据格式"); }
protected void saveData() { System.out.println("保存 CSV 数据到数据库"); }
}
JDK 中 Arrays.sort()、InputStream.read() 都是模板方法模式。
六、观察者模式——事件通知机制
一个对象状态变化,自动通知其他依赖对象。消息队列、事件监听、GUI 事件都是观察者模式。
// 观察者接口
interface Observer {
void update(String message);
}
// 被观察者(主题)
class Subject {
private List<Observer> observers = new ArrayList<>();
public void attach(Observer observer) {
observers.add(observer);
}
public void detach(Observer observer) {
observers.remove(observer);
}
public void notifyObservers(String message) {
for (Observer obs : observers) {
obs.update(message);
}
}
}
// 具体观察者
class EmailObserver implements Observer {
private String name;
public EmailObserver(String name) { this.name = name; }
public void update(String message) {
System.out.println(name + " 收到邮件通知: " + message);
}
}
class SmsObserver implements Observer {
private String name;
public SmsObserver(String name) { this.name = name; }
public void update(String message) {
System.out.println(name + " 收到短信通知: " + message);
}
}
// 使用
Subject subject = new Subject();
subject.attach(new EmailObserver("张三"));
subject.attach(new SmsObserver("李四"));
subject.notifyObservers("订单已发货"); // 所有观察者自动收到通知
Spring 事件监听(实际开发更常用)
// 定义事件
public class OrderEvent extends ApplicationEvent {
private String orderNo;
public OrderEvent(Object source, String orderNo) {
super(source);
this.orderNo = orderNo;
}
public String getOrderNo() { return orderNo; }
}
// 监听器
@Component
public class SmsListener implements ApplicationListener<OrderEvent> {
public void onApplicationEvent(OrderEvent event) {
System.out.println("发送短信通知:订单 " + event.getOrderNo() + " 已创建");
}
}
@Component
public class EmailListener implements ApplicationListener<OrderEvent> {
public void onApplicationEvent(OrderEvent event) {
System.out.println("发送邮件通知:订单 " + event.getOrderNo() + " 已创建");
}
}
// 发布事件
@Service
public class OrderService {
@Autowired
private ApplicationEventPublisher publisher;
public void createOrder(String orderNo) {
System.out.println("创建订单: " + orderNo);
publisher.publishEvent(new OrderEvent(this, orderNo));
// 监听器自动执行,不影响主流程
}
}
七、设计模式选择指南
| 模式 | 适用场景 | 核心思想 |
|---|---|---|
| 单例 | 全局只需要一个对象(配置、连接池) | 控制实例数量 |
| 工厂 | 创建对象逻辑复杂或需要解耦 | 分离创建和使用 |
| 策略 | 多种行为可互换,避免 if-else | 封装变化 |
| 代理 | 给对象增强功能(日志、事务、权限) | 控制访问 |
| 模板方法 | 流程骨架固定,部分步骤可变 | 复用流程 |
| 观察者 | 一对多依赖关系,状态变化通知 | 解耦通知 |
八、如何在项目中运用
不要为了用设计模式而用设计模式。遇到以下情况再考虑:
if-else 太多 → 考虑策略模式
需要加日志/事务 → 考虑代理模式
流程固定但步骤不同 → 模板方法模式
全局只用一个实例 → 单例模式
对象创建复杂 → 工厂模式
需要事件通知 → 观察者模式
设计模式的最高境界是:用了但感觉没用——代码自然、干净、好维护。
💡 觉得有用的话,点赞 + 关注【张老师技术栈】吧!每周更新 Java/Python/爬虫 实战干货,不让你白来。
所有评论(0)