设计模式实战指南

设计模式实战指南(Java 版)

设计模式是 Java 面试必考、代码质量的核心话题。本文不讲 23 种模式全部背诵,而是聚焦生产中最常用的 12 种:单例、工厂、建造者、策略、模板方法、观察者、责任链、代理、适配器、装饰器、状态、门面------每种带实战场景、Java 代码和面试要点。


一、设计模式是什么

1.1 定义

设计模式是前人总结的、针对特定问题的可复用解决方案------不是代码模板,是"解决问题的思路"。

什么时候用:

  • 代码开始变乱(if-else 爆炸、重复代码、难扩展);
  • 需求经常变("加一种支付方式"这类需求反复出现)。

什么时候别用:

  • 简单逻辑硬套模式 = 过度设计;
  • 一个 if 能解决的事非要造 5 个类。

核心原则(SOLID 简版):

  • 单一职责:一个类只干一件事;
  • 开闭原则:对扩展开放,对修改关闭(设计模式的灵魂);
  • 依赖倒置:面向接口编程。

二、创建型模式(怎么 new 对象)

2.1 单例模式(面试必考)

场景:全局唯一对象------线程池、配置、连接池。

饿汉式(简单,线程安全):

java 复制代码
public class ConfigManager {
    private static final ConfigManager INSTANCE = new ConfigManager();
    private ConfigManager() {}

    public static ConfigManager getInstance() { return INSTANCE; }
}

双重检查懒汉式(延迟加载 + 线程安全):

java 复制代码
public class ConfigManager {
    private static volatile ConfigManager instance;   // volatile 必须

    private ConfigManager() {}

    public static ConfigManager getInstance() {
        if (instance == null) {                        // 1. 快速判断
            synchronized (ConfigManager.class) {
                if (instance == null) {                // 2. 加锁后再判断
                    instance = new ConfigManager();
                }
            }
        }
        return instance;
    }
}

为什么 volatile :防止 new 指令重排(先赋值后初始化),别的线程拿到半初始化对象。

枚举单例(最简洁,防反射/序列化破坏):

java 复制代码
public enum ConfigManager {
    INSTANCE;
    public void load() { ... }
}

面试追问:

  • 反射能破坏单例吗?→ 能,枚举除外;
  • 序列化会破坏吗?→ 会,重写 readResolve。

2.2 工厂模式

场景:创建对象逻辑复杂、按条件创建不同子类。

简单工厂:

java 复制代码
public class PaymentFactory {
    public static Payment create(String type) {
        return switch (type) {
            case "alipay" -> new AlipayPayment();
            case "wechat" -> new WechatPayment();
            default -> throw new IllegalArgumentException("未知支付方式");
        };
    }
}

问题 :加新支付方式要改工厂------违反开闭。工厂方法模式把创建逻辑下放到子类,但简单场景下简单工厂够用。

实战推荐 :Spring 的 @Bean + 策略注入就是工厂的现代替代(见策略模式)。

2.3 建造者模式

场景:对象字段多、必填可选混合、不可变对象。

java 复制代码
public class Order {
    private final String orderNo;     // 必填
    private final Long userId;        // 必填
    private final String couponCode;  // 可选
    private final String remark;      // 可选

    private Order(Builder builder) {
        this.orderNo = builder.orderNo;
        this.userId = builder.userId;
        this.couponCode = builder.couponCode;
        this.remark = builder.remark;
    }

    public static class Builder {
        private String orderNo;
        private Long userId;
        private String couponCode;
        private String remark;

        public Builder(String orderNo, Long userId) {   // 必填放构造器
            this.orderNo = orderNo;
            this.userId = userId;
        }
        public Builder couponCode(String v) { this.couponCode = v; return this; }
        public Builder remark(String v) { this.remark = v; return this; }

        public Order build() {
            return new Order(this);
        }
    }
}

// 使用
Order order = new Order.Builder("20260930", 1001L)
    .couponCode("DISCOUNT50")
    .remark("尽快发货")
    .build();

注意 :@Builder(Lombok)就是自动生成建造者------理解原理即可,开发直接用注解。


三、行为型模式(对象怎么协作)

3.1 策略模式(生产使用率最高)

场景:同一行为有多种实现,运行时选择。典型:支付方式、优惠计算、登录方式。

核心:接口 + 多实现 + 选择器。

java 复制代码
// 1. 策略接口
public interface PaymentStrategy {
    void pay(BigDecimal amount);
}

// 2. 多个实现
@Component("alipay")
public class AlipayStrategy implements PaymentStrategy {
    public void pay(BigDecimal amount) {
        System.out.println("支付宝支付: " + amount);
    }
}

@Component("wechat")
public class WechatStrategy implements PaymentStrategy {
    public void pay(BigDecimal amount) {
        System.out.println("微信支付: " + amount);
    }
}

// 3. 选择器(用 Spring 注入所有策略,消除 if-else)
@Component
public class PaymentContext {
    private final Map<String, PaymentStrategy> strategies;

    // Spring 自动把同接口的 Bean 注入 Map(key = Bean 名)
    public PaymentContext(Map<String, PaymentStrategy> strategies) {
        this.strategies = strategies;
    }

    public void pay(String type, BigDecimal amount) {
        PaymentStrategy strategy = strategies.get(type);
        if (strategy == null) throw new IllegalArgumentException("未知支付方式");
        strategy.pay(amount);
    }
}

核心价值 :新增支付方式 → 加一个类(@Component),不用改任何现有代码------完美的开闭原则。

面试考点:策略模式和 if-else 的取舍、和状态模式的区别(策略是算法切换,状态是行为随状态变化)。

3.2 模板方法模式

场景:流程骨架固定,步骤实现可变。

java 复制代码
public abstract class DataImportTemplate {

    // 模板方法:定义流程(final 防子类改流程)
    public final void importData() {
        validate();          // 1. 校验(子类实现)
        parse();             // 2. 解析(子类实现)
        save();              // 3. 保存(子类实现)
        notifyResult();      // 4. 通知(公共实现)
    }

    protected abstract void validate();
    protected abstract void parse();
    protected abstract void save();

    private void notifyResult() {   // 公共逻辑固定
        System.out.println("导入完成");
    }
}

// 用户导入
public class UserImport extends DataImportTemplate {
    protected void validate() { /* 校验用户数据 */ }
    protected void parse()   { /* 解析 Excel */ }
    protected void save()    { /* 批量保存 */ }
}

场景 :导入导出、定时任务(Spring 的 ScheduledTask 思路)、消息处理流程。

3.3 观察者模式

场景:一个事件触发多个动作。如订单创建 → 发短信 + 发邮件 + 加积分。

用 Spring 事件(生产标准做法):

java 复制代码
// 1. 事件
public class OrderCreatedEvent extends ApplicationEvent {
    private final Order order;
    public OrderCreatedEvent(Object source, Order order) {
        super(source);
        this.order = order;
    }
    public Order getOrder() { return order; }
}

// 2. 发布(业务代码里)
@Service
public class OrderService {
    @Autowired
    private ApplicationEventPublisher publisher;

    public void createOrder(Order order) {
        // ... 业务逻辑
        publisher.publishEvent(new OrderCreatedEvent(this, order));  // 发事件
    }
}

// 3. 监听者(多个,互不干扰)
@Component
public class SmsListener {
    @EventListener
    public void onOrderCreated(OrderCreatedEvent event) {
        sendSms(event.getOrder().getUserPhone());
    }
}

@Component
public class PointsListener {
    @EventListener
    public void onOrderCreated(OrderCreatedEvent event) {
        addPoints(event.getOrder().getUserId(), 100);
    }
}

核心价值 :新增"发优惠券"动作 → 加一个 @EventListener 类,不动 OrderService------解耦。

面试考点 :观察者和发布订阅的区别(观察者是同步直连,发布订阅可通过 MQ 解耦);Spring 事件默认同步,可用 @Async 异步。

3.4 责任链模式

场景:多个处理器依次处理,直到有人处理。典型:审批流、过滤器、校验链。

java 复制代码
// 1. 处理器抽象
public abstract class OrderHandler {
    protected OrderHandler next;

    public OrderHandler setNext(OrderHandler next) {
        this.next = next;
        return next;
    }

    public final void handle(Order order) {
        if (canHandle(order)) {
            doHandle(order);
        } else if (next != null) {
            next.handle(order);
        }
    }

    protected abstract boolean canHandle(Order order);
    protected abstract void doHandle(Order order);
}

// 2. 具体处理器
public class VIPHandler extends OrderHandler {
    protected boolean canHandle(Order o) { return o.getAmount() > 10000; }
    protected void doHandle(Order o) { System.out.println("VIP 订单,人工审批"); }
}

public class NormalHandler extends OrderHandler {
    protected boolean canHandle(Order o) { return o.getAmount() <= 10000; }
    protected void doHandle(Order o) { System.out.println("普通订单,自动通过"); }
}

// 3. 组链
OrderHandler chain = new VIPHandler()
    .setNext(new NormalHandler());
chain.handle(order);

Spring 实现:把多个 Handler 注入 List,按 @Order 排序依次调用------Spring MVC 的拦截器、Spring Security 的 Filter 都是责任链思路。

3.5 状态模式

场景:对象行为随内部状态变化。典型:订单状态机(待支付→已支付→已发货→已完成)。

java 复制代码
public interface OrderState {
    void handle(OrderContext context);
}

public class PendingPaymentState implements OrderState {
    public void handle(OrderContext context) {
        System.out.println("待支付 → 确认支付");
        context.setState(new PaidState());
    }
}

public class PaidState implements OrderState {
    public void handle(OrderContext context) {
        System.out.println("已支付 → 发货");
        context.setState(new ShippedState());
    }
}

public class OrderContext {
    private OrderState state = new PendingPaymentState();
    public void setState(OrderState state) { this.state = state; }
    public void next() { state.handle(this); }
}

// 使用
OrderContext order = new OrderContext();
order.next();   // 待支付 → 已支付
order.next();   // 已支付 → 已发货

和策略模式区别(面试常问) :状态模式的状态会自动流转 (处理完换状态);策略模式是外部选择算法。


四、结构型模式(怎么组织类)

4.1 代理模式

场景:不直接访问目标,通过代理加控制。典型:AOP、延迟加载、权限控制。

静态代理(一个代理一个目标):

java 复制代码
public interface UserService { void save(); }

public class UserServiceImpl implements UserService {
    public void save() { System.out.println("保存用户"); }
}

// 代理:加日志
public class UserServiceProxy implements UserService {
    private final UserService target;

    public UserServiceProxy(UserService target) { this.target = target; }

    public void save() {
        System.out.println("日志: 开始保存");
        target.save();
        System.out.println("日志: 保存完成");
    }
}

动态代理(生产主力) :Spring AOP 就是动态代理------@Transactional、@Async 底层都是它。

java 复制代码
// JDK 动态代理(目标必须实现接口)
UserService proxy = (UserService) Proxy.newProxyInstance(
    UserServiceImpl.class.getClassLoader(),
    new Class[]{UserService.class},
    (p, method, args) -> {
        System.out.println("前置逻辑");
        Object result = method.invoke(new UserServiceImpl(), args);
        System.out.println("后置逻辑");
        return result;
    }
);

Spring AOP 实战(接口耗时统计):

java 复制代码
@Aspect
@Component
public class TimeAspect {

    @Around("@annotation(com.example.Timed)")   // 加 @Timed 的方法自动统计
    public Object measure(ProceedingJoinPoint pjp) throws Throwable {
        long start = System.currentTimeMillis();
        try {
            return pjp.proceed();
        } finally {
            long cost = System.currentTimeMillis() - start;
            System.out.println(pjp.getSignature() + " 耗时: " + cost + "ms");
        }
    }
}

面试考点:JDK 代理 vs CGLIB(JDK 要求接口,CGLIB 可代理类;Spring Boot 默认 CGLIB)。

4.2 适配器模式

场景:接口不兼容时转换。典型:第三方 SDK 统一封装、老接口兼容。

java 复制代码
// 第三方支付接口(不能改)
public class ThirdPartyPayApi {
    public String doThirdPay(String params) { return "success"; }
}

// 统一接口
public interface PayGateway {
    String pay(String orderNo, BigDecimal amount);
}

// 适配器:把第三方接口包装成统一接口
public class ThirdPartyAdapter implements PayGateway {
    private final ThirdPartyPayApi api = new ThirdPartyPayApi();

    public String pay(String orderNo, BigDecimal amount) {
        String params = buildParams(orderNo, amount);   // 转换参数格式
        return api.doThirdPay(params);
    }
}

和代理模式区别 :代理加控制不改接口,适配器转换接口。

4.3 装饰器模式

场景:动态增强功能,比继承灵活。典型:IO 流(BufferedReader 包装 FileReader)、日志增强。

java 复制代码
public interface Report { String render(); }

public class BasicReport implements Report {
    public String render() { return "基础报表"; }
}

// 装饰器:加 Excel 导出
public class ExcelReportDecorator implements Report {
    private final Report report;
    public ExcelReportDecorator(Report report) { this.report = report; }

    public String render() {
        return report.render() + " + Excel 导出";
    }
}

// 使用:一层层包
Report report = new ExcelReportDecorator(new PdfReportDecorator(new BasicReport()));

和适配器区别 :适配器转换接口,装饰器增强原接口。

4.4 门面模式(外观模式)

场景:复杂子系统的统一入口。典型:Service 层包装一堆 Mapper。

java 复制代码
// 下单这个"门面"隐藏了内部一堆组件
@Service
public class OrderFacade {
    @Autowired private OrderService orderService;
    @Autowired private StockService stockService;
    @Autowired private CouponService couponService;

    public Order createOrder(OrderDTO dto) {
        couponService.useCoupon(dto.getCouponId());     // 用券
        stockService.deduct(dto.getSkuId(), dto.getNum()); // 扣库存
        return orderService.create(dto);                 // 建订单
    }
}

调用方只依赖 OrderFacade,不用知道内部细节。


五、设计模式在框架中的体现(面试加分)

框架/技术 用到的模式
Spring IoC 工厂模式、单例
Spring AOP 代理模式
Spring 事件 观察者模式
MyBatis 建造者(SqlSessionFactoryBuilder)、代理(Mapper)
JDBC DriverManager 工厂模式
Java IO 装饰器模式
Tomcat 责任链(Filter)
Spring Security 责任链(FilterChain)
Servlet 模板方法、门面

面试回答技巧:"Spring 的 @Transactional 底层是 AOP,AOP 底层是动态代理,动态代理有 JDK 和 CGLIB 两种实现"------一条线串起来,比背模式定义有用。


六、实战:把 if-else 重构为策略模式

重构前(新需求要改代码):

java 复制代码
public BigDecimal calcDiscount(String userType, BigDecimal amount) {
    if ("vip".equals(userType)) {
        return amount.multiply(new BigDecimal("0.9"));
    } else if ("svip".equals(userType)) {
        return amount.multiply(new BigDecimal("0.8"));
    } else if ("black".equals(userType)) {
        return amount.multiply(new BigDecimal("0.5"));
    } else {
        return amount;
    }
}

重构后(新需求只要加类):

java 复制代码
public interface DiscountStrategy {
    boolean support(String userType);
    BigDecimal calc(BigDecimal amount);
}

@Component
public class VipDiscount implements DiscountStrategy {
    public boolean support(String t) { return "vip".equals(t); }
    public BigDecimal calc(BigDecimal a) { return a.multiply(new BigDecimal("0.9")); }
}
// ... SVIP、Black 同理

@Component
public class DiscountContext {
    private final List<DiscountStrategy> strategies;

    public DiscountContext(List<DiscountStrategy> strategies) {
        this.strategies = strategies;
    }

    public BigDecimal calc(String userType, BigDecimal amount) {
        return strategies.stream()
            .filter(s -> s.support(userType))
            .findFirst()
            .orElseThrow()
            .calc(amount);
    }
}

收益:新增"钻石会员" = 新增一个类 + @Component,零改动现有代码。


七、设计模式选择速查

需求 用哪个
全局唯一对象 单例
按条件创建对象 工厂
复杂对象构建 建造者
算法可切换 策略
流程固定步骤可变 模板方法
一事件多响应 观察者
多级处理 责任链
状态流转 状态
加控制/日志 代理
接口不兼容 适配器
动态增强 装饰器
统一入口 门面

本章小结

设计模式的本质是应对变化:策略消除 if-else、观察者解耦事件、责任链组织处理器、代理加横切逻辑。生产中最值钱的是策略、观察者、代理(AOP)、责任链四个------把它们吃透,再配合 Spring 的自动注入,就是标准的企业级写法。

下一篇讲数据结构与算法------大厂面试的硬通货。

(全文完)

相关推荐
电子科技圈8 小时前
基于车载SerDes和OpenGMSL协议的SoC设计与验证:行业高频问题解析
设计模式·车载系统·软件构建·设计规范
ClinicTech10 小时前
高速冷冻离心机的温控系统与安全保护逻辑解析
设计模式·架构·健康医疗·设计语言
GISMagic14 小时前
3.《设计模式》:软件设计与解耦能力提炼
设计模式·ai coding
染指111014 小时前
131.Agent-Agent设计模式-MAS多智能体系统(Multi-Agent-System)
人工智能·设计模式·langchain·agent·agents
ShyanZh1 天前
【Python3基础】13-Python 常用设计模式
开发语言·python·设计模式
YYYing.2 天前
【设计模式系列 (五) 】原型模式
开发语言·后端·设计模式·原型模式·c/c++
长按助力退休2 天前
设计模式实战,这 5 个最常用
设计模式
Carl_奕然2 天前
【智能体】Agent的四种设计模式之:Multi-Agent Collaboration
设计模式
Lost of 程序猿2 天前
建造者模式实战:告别“十参数构造函数“的数据导出任务
后端·设计模式·c#·asp.net