Java 设计模式:原理、框架应用与实战全解析|得物技术

一、概述

简介

设计模式(Design Pattern)是前辈们对代码开发经验的总结,它不是语法规定,是解决特定问题的一系列思想,是面向对象设计原则的具象化实现, 是解决 "需求变更" 与 "系统复杂度" 矛盾的标准化方案 ------ 并非孤立的 "代码模板",而是 "高内聚、低耦合" 思想的落地工具。其核心价值在于提升代码的可复用性、可维护性、可读性、稳健性及安全性。

1994 年,GoF(Gang of Four:Erich Gamma、Richard Helm、Ralph Johnson、John Vlissides)合著的《Design Patterns - Elements of Reusable Object-Oriented Software》(中文译名《设计模式 - 可复用的面向对象软件元素》)出版,收录 23 种经典设计模式,奠定该领域的行业标准,即 "GoF 设计模式"。

核心思想

  • 对接口编程,而非对实现编程
  • 优先使用对象组合,而非继承
  • 灵活适配需求:简单程序无需过度设计,大型项目 / 框架必须借助模式优化架构

组件生命周期

模式类型 核心关注点 生命周期阶段 代表模式
创建型模式 对象创建机制 (解耦创建与使用) 组件的创建 单例、工厂方法、抽象工厂、原型、建造者
结构型模式 对象 / 类的组合方式 组件的使用 代理、适配器、装饰器、外观、享元、桥接、组合、过滤器
行为型模式 对象 / 类的运行时协作流程 组件的交互与销毁 策略、观察者、责任链、模板方法、命令、状态、中介者、迭代器、访问者、备忘录、解释器

七大设计原则

原则名称 核心定义 关联模式 实际开发决策逻辑
开闭原则(OCP) 对扩展开放,对修改关闭 (新增功能通过扩展类实现,不修改原有代码) 所有模式的终极目标 新增需求优先考虑 "加类",而非 "改类"
依赖倒转原则(DIP) 依赖抽象而非具体实现 (面向接口编程,不依赖具体类) 工厂、策略、桥接 类的依赖通过接口注入,而非直接 new 具体类
合成复用原则(CRP) 优先使用组合 / 聚合,而非继承 (降低耦合,提升灵活性) 装饰器、组合、桥接 复用功能时,先考虑 "组合",再考虑 "继承"
单一职责原则(SRP) 一个类仅负责一项核心职责 (避免 "万能类") 策略、适配器、装饰器 当一个类有多个修改原因时,立即拆分
接口隔离原则(ISP) 使用多个专用接口替代单一万能接口 (降低类与接口的耦合) 适配器、代理 接口方法拆分到 "最小粒度",避免实现类冗余
里氏代换原则(LSP) 子类可替换父类,且不破坏原有逻辑 (继承复用的核心前提) 模板方法、策略 子类重写父类方法时,不能改变父类契约
迪米特法则(LOD) 实体应尽量少与其他实体直接交互 (通过中间者解耦) 中介者、外观、责任链 两个无直接关联的类,通过第三方间接交互

二、原理与框架应用

创建型模式

为什么用创建型模式?

  • 创建型模式关注点"怎样创建出对象?""将对象的创建与使用分离"
  • 降低系统的耦合度
  • 使用者无需关注对象的创建细节
  • 对象的创建由相关的工厂来完成;(各种工厂模式)
  • 对象的创建由一个建造者来完成;(建造者模式)
  • 对象的创建由原来对象克隆完成;(原型模式)
  • 对象始终在系统中只有一个实例;(单例模式)

创建型模式之单例模式

单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一,提供了一种创建对象的最佳方式。这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。

意图: 保证一个类仅有一个实例,并提供一个访问它的全局访问点。

主要解决: 一个全局使用的类频繁地创建与销毁。

何时使用: 当您想控制实例数目,节省系统资源的时候。

如何解决: 判断系统是否已经有这个单例,如果有则返回,如果没有则创建。

优点:

1、在内存里只有一个实例,减少了内存的开销,尤其是频繁的创建和销毁实例(比如首页页面缓存)。

2、避免对资源的多重占用(比如写文件操作)。

缺点:

没有接口,不能继承,与单一职责原则冲突,一个类应该只关心内部逻辑,而不关心外面怎么样来实例化。

使用场景:

1、要求生产唯一序列号。

2、多线程中的线程池。

3、创建的一个对象需要消耗的资源过多,比如 I/O 与数据库的连接等。

4、系统环境信息(System.getProperties())。

单例模式四种实现方案

饿汉式

arduino 复制代码
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
 * 饿汉式单例(线程安全)
 * 核心原理:依赖类加载机制(JVM保证类初始化时线程安全)
 * 适用场景:实例占用资源小、启动时初始化可接受的场景
 */
public class LibifuTestSingleton {
    private static final Logger log = LoggerFactory.getLogger(LibifuTestSingleton.class);


    // 类加载时直接初始化实例(无延迟加载)
    private static final LibifuTestSingleton INSTANCE = new LibifuTestSingleton();
    // 私有构造器(禁止外部实例化)
    private LibifuTestSingleton() {
        log.info("LibifuTestSingleton 实例初始化完成");
    }
    // 全局访问点(无锁,高效)
    public static LibifuTestSingleton getInstance() {
        return INSTANCE;
    }
    // 业务方法示例
    public void doBusiness() {
        log.info("饿汉式单例(LibifuTestSingleton)执行业务逻辑");
    }
}

懒汉式

arduino 复制代码
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
 * 懒汉式单例(线程安全)
 * 核心原理:第一次调用时初始化,synchronized保证线程安全
 * 适用场景:实例使用频率极低、无性能要求的场景
 */
public class LibifuTestLazySingleton {
    private static final Logger log = LoggerFactory.getLogger(LibifuTestLazySingleton.class);


    // 私有静态实例(初始为null,延迟加载)
    private static LibifuTestLazySingleton instance;
    // 私有构造器(禁止外部实例化)
    private LibifuTestLazySingleton() {
        log.info("LibifuTestLazySingleton 实例初始化完成");
    }
    // 同步方法(保证多线程下唯一实例)
    public static synchronized LibifuTestLazySingleton getInstance() {
        if (instance == null) {
            instance = new LibifuTestLazySingleton();
        }
        return instance;
    }
    // 业务方法示例
    public void doBusiness() {
        log.info("懒汉式单例(LibifuTestLazySingleton)执行业务逻辑");
    }
}

双检锁 (DCL,JDK1.5+)

csharp 复制代码
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
 * 双检锁单例(线程安全,高效)
 * 核心原理:volatile禁止指令重排序,双重校验+类锁保证唯一性
 * 适用场景:大多数高并发场景
 */
public class LibifuTestDclSingleton {
    private static final Logger log = LoggerFactory.getLogger(LibifuTestDclSingleton.class);


    // volatile关键字:禁止instance = new LibifuTestDclSingleton()指令重排序
    private volatile static LibifuTestDclSingleton instance;
    // 私有构造器(禁止外部实例化,含防反射攻击)
    private LibifuTestDclSingleton() {
        log.info("LibifuTestDclSingleton 实例初始化完成");
        // 防反射攻击:若实例已存在,直接抛出异常
        if (instance != null) {
            throw new IllegalStateException("单例实例已存在,禁止重复创建");
        }
    }
    // 全局访问点(双重校验+类锁,兼顾线程安全与效率)
    public static LibifuTestDclSingleton getInstance() {
        // 第一次校验:避免频繁加锁(提高效率)
        if (instance == null) {
            // 类锁:保证同一时刻只有一个线程进入实例创建逻辑
            synchronized (LibifuTestDclSingleton.class) {
                // 第二次校验:确保唯一实例(防止多线程并发绕过第一次校验)
                if (instance == null) {
                    instance = new LibifuTestDclSingleton();
                }
            }
        }
        return instance;
    }
    // 防序列化漏洞:反序列化时返回已有实例(而非创建新实例)
    private Object readResolve() {
        return getInstance();
    }
    // 业务方法示例
    public void doBusiness() {
        log.info("双检锁单例(LibifuTestDclSingleton)执行业务逻辑");
    }
}

枚举单例(JDK1.5+)

arduino 复制代码
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
 * 枚举单例(天然线程安全、防反射、防序列化)
 * 核心原理:枚举类的实例由JVM管理,天然唯一
 * 适用场景:安全性要求极高的场景(如配置中心、加密工具类)
 */
public enum LibifuTestEnumSingleton {
    INSTANCE;
    private static final Logger log = LoggerFactory.getLogger(LibifuTestEnumSingleton.class);
    // 枚举构造器(默认私有,无需显式声明)
    LibifuTestEnumSingleton() {
        log.info("LibifuTestEnumSingleton 实例初始化完成");
    }
    // 业务方法示例
    public void doBusiness() {
        log.info("枚举单例(LibifuTestEnumSingleton)执行业务逻辑");
    }
}

框架应用

Spring 框架中 Bean 默认作用域为singleton(单例),核心通过AbstractBeanFactory类的缓存机制 + 单例创建逻辑实现 ------ 确保每个 Bean 在 Spring 容器中仅存在一个实例,且由容器统一管理创建、缓存与销毁,降低对象频繁创建销毁的资源开销,契合单例模式 "唯一实例 + 全局访问" 的核心思想。

核心逻辑:Bean 创建后存入singletonObjects(单例缓存池),后续获取时优先从缓存读取,未命中则触发创建流程,同时通过同步机制保证多线程安全。

以下选取AbstractBeanFactory中实现单例 Bean 获取的核心代码片段:

kotlin 复制代码
// 1. 对外暴露的获取Bean的公共接口,接收Bean名称参数
@Override
public Object getBean(String name) throws BeansException {
    // 2. 委托doGetBean方法实现具体逻辑,参数分别为:Bean名称、所需类型(null表示不指定)、构造参数(null)、是否仅类型检查(false)
    return doGetBean(name, null, null, false);
}
// 3. 核心获取Bean的实现方法,泛型T保证类型安全
@SuppressWarnings("unchecked")
protected <T> T doGetBean(
        String name, Class<T> requiredType, Object[] args, boolean typeCheckOnly) throws BeansException {
    // 4. 处理Bean名称:转换别名、去除FactoryBean前缀(如&),得到原始Bean名称
    String beanName = transformedBeanName(name);
    // 5. 从单例缓存中获取Bean实例(核心:优先复用已有实例)
    Object sharedInstance = getSingleton(beanName);
    // 6. 缓存命中(存在单例实例)且无构造参数(无需重新创建)
    if (sharedInstance != null && args == null) {
        // 7. 处理特殊Bean(如FactoryBean):如果是FactoryBean,返回其getObject()创建的实例,而非FactoryBean本身
        T bean = (T) getObjectForBeanInstance(sharedInstance, name, beanName, null);
    } else {
        // 8. 缓存未命中或需创建新实例(非单例、原型等作用域)的逻辑(此处省略,聚焦单例)
    }
    // 9. 返回最终的Bean实例(类型转换后)
    return (T) bean;
}
// 10. 从单例缓存中获取实例的核心方法,allowEarlyReference表示是否允许早期引用(循环依赖场景)
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    // 11. 从一级缓存(singletonObjects)获取已完全初始化的单例实例(key=Bean名称,value=Bean实例)
    Object singletonObject = this.singletonObjects.get(beanName);


    // 12. 缓存未命中,且当前Bean正在创建中(解决循环依赖)
    if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
        // 13. 对一级缓存加锁,保证多线程安全(避免并发创建多个实例)
        synchronized (this.singletonObjects) {
            // 14. 从二级缓存(earlySingletonObjects)获取早期暴露的实例(未完全初始化,仅解决循环依赖)
            singletonObject = this.earlySingletonObjects.get(beanName);


            // 15. 二级缓存未命中,且允许早期引用
            if (singletonObject == null && allowEarlyReference) {
                // 16. 从三级缓存(singletonFactories)获取Bean的工厂对象(用于创建早期实例)
                ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);


                // 17. 工厂对象存在,通过工厂创建早期实例
                if (singletonFactory != null) {
                    singletonObject = singletonFactory.getObject();
                    // 18. 将早期实例存入二级缓存,同时移除三级缓存(避免重复创建)
                    this.earlySingletonObjects.put(beanName, singletonObject);
                    this.singletonFactories.remove(beanName);
                }
            }
        }
    }
    // 19. 返回单例实例(可能是完全初始化的,也可能是早期实例)
    return singletonObject;
}

入口: getBean(String name)是获取 Bean 的入口,委托doGetBean实现细节;

名称处理: transformedBeanName统一 Bean 名称格式,避免别名、FactoryBean 前缀导致的识别问题;

缓存优先: 通过getSingleton从三级缓存(singletonObjects→earlySingletonObjects→singletonFactories)获取实例,优先复用已有实例,契合单例模式核心;

线程安全: 对单例缓存加锁,防止多线程并发创建多个实例;

特殊处理: getObjectForBeanInstance区分普通 Bean 和 FactoryBean,确保返回用户预期的实例。

整个流程围绕 "缓存复用 + 安全创建" 实现 Spring 单例 Bean 的管理,是单例模式在框架级的经典落地。

结构型模式

为什么用结构型模式?

  • 结构型模式关注点"怎样组合对象/类"
  • 类结构型模式关心类的组合,由多个类可以组合成一个更大的(继承)
  • 对象结构型模式关心类与对象的组合,通过关联关系在一个类中定义另一个类的实例对象(组合)根据"合成复用原则",在系统中尽量使用关联关系来替代继承关系,因此大部分结构型模式都是对象结构型模式。
  • 适配器模式(Adapter Pattern):两个不兼容接口之间适配的桥梁
  • 桥接模式(Bridge Pattern):相同功能抽象化与实现化解耦,抽象与实现可以独立升级
  • 过滤器模式(Filter、Criteria Pattern):使用不同的标准来过滤一组对象
  • 组合模式(Composite Pattern):相似对象进行组合,形成树形结构
  • 装饰器模式(Decorator Pattern):向一个现有的对象添加新的功能,同时又不改变其结构
  • 外观模式(Facade Pattern):向现有的系统添加一个接口,客户端访问此接口来隐藏系统的复杂性
  • 享元模式(Flyweight Pattern):尝试重用现有的同类对象,如果未找到匹配的对象,则创建新对象
  • 代理模式(Proxy Pattern):一个类代表另一个类的功能

结构型模式之外观模式

外观模式(Facade Pattern)为复杂子系统提供统一高层接口,隐藏内部复杂性,简化客户端调用。这种模式涉及到一个单一的类,该类提供了客户端请求的简化方法和对现有系统类方法的委托调用。

意图: 为子系统中的一组接口提供一个一致的界面,外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。

主要解决: 降低访问复杂系统的内部子系统时的复杂度,简化客户端之间的接口。

何时使用:

1、客户端不需要知道系统内部的复杂联系,整个系统只需提供一个"接待员"即可。

2、定义系统的入口。

如何解决: 客户端不与系统耦合,外观类与系统耦合。

优点:

1、减少系统相互依赖。

2、提高灵活性。

3、提高了安全性。

缺点:

不符合开闭原则,如果要改东西很麻烦,继承重写都不合适。

使用场景:

1、JAVA 的三层开发模式

2、分布式系统的网关

外观模式简单应用

程序员这行,主打一个 "代码虐我千百遍,我待键盘如初恋"------ 白天 debug ,深夜改 Bug ,免疫力堪比未加 try-catch 的代码,说崩就崩。现在医院就诊(挂号、缴费、取药等子系统)都是通过 "微信自助程序"来统一入口,下面就使用外观模式简单实现:

子系统组件(就诊各窗口)

arduino 复制代码
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
 * 子系统1:挂号窗口
 */
public class LibifuTestRegisterWindow {
    private static final Logger log = LoggerFactory.getLogger(LibifuTestRegisterWindow.class);
    /**
     * 挂号业务逻辑
     * @param name 患者姓名
     * @param department 就诊科室
     */
    public void register(String name, String department) {
        log.info(" {} 已完成{}挂号,挂号成功", name, department);
    }
}
/**
 * 子系统2:医保缴费窗口
 */
public class LibifuTestPaymentWindow {
    private static final Logger log = LoggerFactory.getLogger(LibifuTestPaymentWindow.class);
    /**
     * 医保结算业务逻辑
     * @param name 患者姓名
     * @param amount 缴费金额(元)
     */
    public void socialInsuranceSettlement(String name, double amount) {
        log.info("{} 医保结算完成,缴费金额:{}元", name, amount);
    }
}
/**
 * 子系统3:取药窗口
 */
public class LibifuTestDrugWindow {
    private static final Logger log = LoggerFactory.getLogger(LibifuTestDrugWindow.class);
    /**
     * 取药业务逻辑
     * @param name 患者姓名
     * @param drugNames 药品名称列表
     */
    public void takeDrug(String name, String... drugNames) {
        String drugs = String.join("、", drugNames);
        log.info("{} 已领取药品:{},取药完成", name, drugs);
    }
}

外观类(微信自助程序)

arduino 复制代码
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
 * 外观类:微信自助程序(统一就诊入口)
 */
public class LibifuTestWeixinHospitalFacade {
    private static final Logger log = LoggerFactory.getLogger(LibifuTestWeixinHospitalFacade.class);
    // 依赖子系统组件(外观类与子系统耦合,客户端与子系统解耦)
    private final LibifuTestRegisterWindow registerWindow;
    private final LibifuTestPaymentWindow paymentWindow;
    private final LibifuTestDrugWindow drugWindow;
    // 构造器初始化子系统(也可通过依赖注入实现)
    public LibifuTestWeixinHospitalFacade() {
        this.registerWindow = new LibifuTestRegisterWindow();
        this.paymentWindow = new LibifuTestPaymentWindow();
        this.drugWindow = new LibifuTestDrugWindow();
    }
    /**
     * 统一就诊流程(封装子系统调用,对外暴露单一接口)
     * @param name 患者姓名
     * @param department 就诊科室
     * @param amount 缴费金额
     * @param drugNames 药品名称
     */
    public void processMedicalService(String name, String department, double amount, String... drugNames) {
        log.info("\n===== {} 发起微信自助就诊流程 =====", name);
        try {
            // 1. 调用挂号子系统
            registerWindow.register(name, department);
            // 2. 调用医保缴费子系统
            paymentWindow.socialInsuranceSettlement(name, amount);
            // 3. 调用取药子系统
            drugWindow.takeDrug(name, drugNames);
            log.info("===== {} 就诊流程全部完成 =====", name);
        } catch (Exception e) {
            log.error("===== {} 就诊流程失败 =====", name, e);
            throw new RuntimeException("就诊流程异常,请重试", e);
        }
    }
}

测试类

arduino 复制代码
/**
 * 客户端:测试外观模式调用
 */
public class LibifuTestFacadeClient {
    public static void main(String[] args) {
        // 1. 获取外观类实例(仅需与外观类交互)
        LibifuTestWeixinHospitalFacade weixinFacade = new LibifuTestWeixinHospitalFacade();
        // 2. 调用统一接口,完成就诊全流程(无需关注子系统细节)
        weixinFacade.processMedicalService(
            "libifu", 
            "呼吸内科", 
            198.5, 
            "布洛芬缓释胶囊", "感冒灵颗粒"
        );
    }
}

运行结果

框架应用

Spring 框架中外观模式(Facade Pattern) 最经典的落地是 ApplicationContext 接口及其实现类。

ApplicationContext 作为「外观类」,封装了底层多个复杂子系统:

  • BeanFactory(Bean 创建 / 管理核心);
  • ResourceLoader(配置文件 / 资源加载);
  • ApplicationEventPublisher(事件发布);
  • MessageSource(国际化消息处理);
  • EnvironmentCapable(环境变量 / 配置解析)。

开发者无需关注这些子系统的交互细节,仅通过 ApplicationContext 提供的统一接口(如 getBean()、publishEvent())即可完成 Spring 容器的所有核心操作 ------ 就像程序员通过「微信自助程序」看病,不用关心医院内部挂号 / 缴费 / 取药的流程,只调用统一入口即可,这正是外观模式「简化复杂系统交互」的核心价值。

以下选取ApplicationContext 、AbstractApplicationContext核心代码片段,展示外观模式的落地逻辑:

arduino 复制代码
package org.springframework.context;
import org.springframework.beans.factory.HierarchicalBeanFactory;
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.core.env.EnvironmentCapable;
import org.springframework.core.io.support.ResourcePatternResolver;
/**
 * 外观接口:整合多个子系统接口,提供统一的容器操作入口
 */
public interface ApplicationContext extends EnvironmentCapable, ListableBeanFactory, 
        HierarchicalBeanFactory, MessageSource, ApplicationEventPublisher, ResourcePatternResolver {
    // 1. 获取应用上下文唯一ID(封装底层无,仅统一暴露)
    String getId();
    // 2. 获取应用名称(统一接口)
    String getApplicationName();
    // 3. 获取上下文显示名称(统一接口)
    String getDisplayName();
    // 4. 获取上下文首次加载的时间戳(统一接口)
    long getStartupDate();
    // 5. 获取父上下文(封装层级BeanFactory的父容器逻辑)
    ApplicationContext getParent();
    // 6. 获取自动装配BeanFactory(封装底层BeanFactory的自动装配能力,核心子系统入口)
    AutowireCapableBeanFactory getAutowireCapableBeanFactory() throws IllegalStateException;
}
java 复制代码
package org.springframework.context.support;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.ConfigurableApplicationContext;
import java.util.concurrent.atomic.AtomicBoolean;
public abstract class AbstractApplicationContext extends DefaultResourceLoader
        implements ConfigurableApplicationContext {
    // ========== 核心1:refresh() - 封装所有子系统的初始化逻辑 ==========
    @Override
    public void refresh() throws BeansException, IllegalStateException {
        synchronized (this.startupShutdownMonitor) {
            // 1. 封装子系统初始化前置检查
            prepareRefresh();
            // 2. 封装BeanFactory子系统的创建/刷新(子类实现具体BeanFactory,如DefaultListableBeanFactory)
            ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
            // 3. 封装BeanFactory子系统的基础配置
            prepareBeanFactory(beanFactory);
            try {
                // xxx 其他源码省略
                // 4. 封装BeanFactory后置处理器执行、事件系统初始化、单例Bean初始化等所有子系统逻辑
                finishBeanFactoryInitialization(beanFactory);
                // 5. 封装容器激活、刷新完成事件发布(子系统收尾)
                finishRefresh();
            } catch (BeansException ex) {
                // 6. 封装子系统初始化失败的回滚逻辑
            }
        }
    }
    // ========== 核心2:getBean() - 封装BeanFactory子系统的调用 + 状态检查 ==========
    @Override
    public <T> T getBean(Class<T> requiredType) throws BeansException {
        // 外观层封装:子系统状态检查(客户端无需关注BeanFactory是否活跃)
        assertBeanFactoryActive();
        // 外观层委托:调用底层BeanFactory子系统的getBean,客户端无需关注BeanFactory具体实现
        return getBeanFactory().getBean(requiredType);
    }
    // ========== 抽象方法:委托子类实现具体BeanFactory获取(屏蔽子系统实现) ==========
    public abstract ConfigurableListableBeanFactory getBeanFactory() throws IllegalStateException;
}

Spring 通过 ApplicationContext(外观接口)和 AbstractApplicationContext(外观实现)封装了其他子系统的复杂逻辑:

  • 客户端只需调用 ApplicationContext.getBean() 即可获取 Bean,无需关注底层 Bean 的缓存、实例化、状态检查等细节;
  • 外观类屏蔽了子系统的复杂度,降低了客户端与底层 BeanFactory 的耦合,符合外观模式的设计思想。

行为型模式

为什么用行为型模式?

  • 行为型模式关注点"怎样运行对象/类"关注类/对象的运行时流程控制。
  • 行为型模式用于描述程序在运行时复杂的流程控制,描述多个类或对象之间怎样相互协作共同完成单个对象都无法单独完成的任务,它涉及算法与对象间职责的分配。
  • 行为型模式分为类行为模式和对象行为模式,前者采用继承机制来在类间分派行为后者采用组合或聚合在对象间分配行为。由于组合关系或聚合关系比继承关系耦合度低,满足"合成复用原则",所以对象行为模式比类行为模式具有更大的灵活性。
  • 模板方法(Template Method)模式:父类定义算法骨架,某些实现放在子类
  • 策略(Strategy)模式:每种算法独立封装,根据不同情况使用不同算法策略
  • 状态(State)模式:每种状态独立封装,不同状态内部封装了不同行为
  • 命令(Command)模式:将一个请求封装为一个对象,使发出请求的责任和执行请求的责任分割开
  • 责任链(Chain of Responsibility)模式:所有处理者封装为链式结构,依次调用
  • 备忘录(Memento)模式:把核心信息抽取出来,可以进行保存
  • 解释器(Interpreter)模式:定义语法解析规则
  • 观察者(Observer)模式:维护多个观察者依赖,状态变化通知所有观察者
  • 中介者(Mediator)模式:取消类/对象的直接调用关系,使用中介者维护
  • 迭代器(Iterator)模式:定义集合数据的遍历规则
  • 访问者(Visitor)模式:分离对象结构,与元素的执行算法

除了模板方法模式和解释器模式是类行为型模式,其他的全部属于对象行为型模式。

行为型模式之策略模式

策略模式(Strategy Pattern)指的是一个类的行为或其算法可以在运行时更改,在策略模式中,我们创建表示各种策略的对象和一个行为随着策略对象改变而改变的 context 对象,策略对象改变 context 对象的执行算法。

意图: 定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换。

主要解决: 在有多种算法相似的情况下,使用 if...else 所带来的复杂和难以维护。

何时使用: 一个系统有许多许多类,而区分它们的只是它们之间的行为。

如何解决: 将这些算法封装成一个一个的类,任意地替换。

优点:

1、算法可以自由切换。

2、避免使用多重条件判断。

3、扩展性良好。

缺点:

1、策略类会增多。

2、所有策略类都需要对外暴露。

使用场景:

1、如果在一个系统里面有许多类,它们之间的区别仅在于它们的行为,那么使用策略模式可以

动态地让一个对象在许多行为中选择一种行为。

2、一个系统需要动态地在几种算法中选择一种。

3、线程池拒绝策略。

策略模式简单应用

在电商支付系统中,都会支持多种支付方式(微信、支付宝、银联),每种支付方式对应一种 "支付策略",客户端可根据用户选择动态切换策略,无需修改支付核心逻辑,下面就使用策略模式简单实现:

策略接口(定义统一算法规范)

php 复制代码
/**
 * 策略接口:支付策略(定义所有支付方式的统一规范)
 */
public interface LibifuTestPaymentStrategy {
    /**
     * 执行支付逻辑
     * @param amount 支付金额(元)
     * @param orderId 订单ID
     * @return 支付结果(成功/失败)
     */
    String pay(double amount, String orderId);
}

具体策略类 1:微信支付

arduino 复制代码
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
 * 具体策略:微信支付(实现支付策略接口)
 */
public class LibifuTestWechatPayStrategy implements LibifuTestPaymentStrategy {
    private static final Logger log = LoggerFactory.getLogger(LibifuTestWechatPayStrategy.class);
    @Override
    public String pay(double amount, String orderId) {
        log.info("【微信支付】开始处理订单:{},金额:{}元", orderId, amount);
        // 模拟微信支付核心逻辑(签名、调用微信接口等)
        boolean isSuccess = true; // 模拟支付成功
        if (isSuccess) {
            String result = String.format("【微信支付】订单%s支付成功,金额:%.2f元", orderId, amount);
            log.info(result);
            return result;
        } else {
            String result = String.format("【微信支付】订单%s支付失败", orderId);
            log.error(result);
            return result;
        }
    }
}

具体策略类 2:支付宝支付

arduino 复制代码
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
 * 具体策略:支付宝支付(实现支付策略接口)
 */
public class LibifuTestAlipayStrategy implements LibifuTestPaymentStrategy {
    private static final Logger log = LoggerFactory.getLogger(LibifuTestAlipayStrategy.class);
    @Override
    public String pay(double amount, String orderId) {
        log.info("【支付宝支付】开始处理订单:{},金额:{}元", orderId, amount);
        // 模拟支付宝支付核心逻辑(验签、调用支付宝接口等)
        boolean isSuccess = true; // 模拟支付成功
        if (isSuccess) {
            String result = String.format("【支付宝支付】订单%s支付成功,金额:%.2f元", orderId, amount);
            log.info(result);
            return result;
        } else {
            String result = String.format("【支付宝支付】订单%s支付失败", orderId);
            log.error(result);
            return result;
        }
    }
}

具体策略类 3:银联支付

arduino 复制代码
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
 * 具体策略:银联支付(实现支付策略接口)
 */
public class LibifuTestUnionPayStrategy implements LibifuTestPaymentStrategy {
    private static final Logger log = LoggerFactory.getLogger(LibifuTestUnionPayStrategy.class);
    @Override
    public String pay(double amount, String orderId) {
        log.info("【银联支付】开始处理订单:{},金额:{}元", orderId, amount);
        // 模拟银联支付核心逻辑(加密、调用银联接口等)
        boolean isSuccess = true; // 模拟支付成功
        if (isSuccess) {
            String result = String.format("【银联支付】订单%s支付成功,金额:%.2f元", orderId, amount);
            log.info(result);
            return result;
        } else {
            String result = String.format("【银联支付】订单%s支付失败", orderId);
            log.error(result);
            return result;
        }
    }
}

上下文类(封装策略调用,屏蔽算法细节)

arduino 复制代码
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
 * 上下文类:支付上下文(持有策略对象,提供统一调用入口)
 * 作用:客户端仅与上下文交互,无需直接操作具体策略
 */
public class LibifuTestPaymentContext {
    private static final Logger log = LoggerFactory.getLogger(LibifuTestPaymentContext.class);
    // 持有策略对象(可动态替换)
    private LibifuTestPaymentStrategy paymentStrategy;
    /**
     * 构造器:初始化支付策略
     * @param paymentStrategy 具体支付策略
     */
    public LibifuTestPaymentContext(LibifuTestPaymentStrategy paymentStrategy) {
        this.paymentStrategy = paymentStrategy;
    }
    /**
     * 动态切换支付策略
     * @param paymentStrategy 新的支付策略
     */
    public void setPaymentStrategy(LibifuTestPaymentStrategy paymentStrategy) {
        log.info("【支付上下文】切换支付策略:{}", paymentStrategy.getClass().getSimpleName());
        this.paymentStrategy = paymentStrategy;
    }
    /**
     * 统一支付入口(屏蔽策略细节,对外暴露简洁方法)
     * @param amount 支付金额
     * @param orderId 订单ID
     * @return 支付结果
     */
    public String executePay(double amount, String orderId) {
        log.info("【支付上下文】开始处理订单{}的支付请求", orderId);
        return paymentStrategy.pay(amount, orderId);
    }
}

测试类

ini 复制代码
/**
 * 客户端:测试策略模式(动态切换支付方式)
 */
public class LibifuTestStrategyClient {
    public static void main(String[] args) {
        // 1. 订单信息
        String orderId = "ORDER_20251213_001";
        double amount = 199.99;
        // 2. 选择微信支付策略
        LibifuTestPaymentContext paymentContext = new LibifuTestPaymentContext(new LibifuTestWechatPayStrategy());
        String wechatResult = paymentContext.executePay(amount, orderId);
        System.out.println(wechatResult);
        // 3. 动态切换为支付宝支付策略
        paymentContext.setPaymentStrategy(new LibifuTestAlipayStrategy());
        String alipayResult = paymentContext.executePay(amount, orderId);
        System.out.println(alipayResult);
        // 4. 动态切换为银联支付策略
        paymentContext.setPaymentStrategy(new LibifuTestUnionPayStrategy());
        String unionPayResult = paymentContext.executePay(amount, orderId);
        System.out.println(unionPayResult);
    }
}

运行结果

框架应用

在Spring 中 ,ResourceLoader 接口及实现类是策略模式的典型落地:

  • 策略接口:ResourceLoader(定义 "加载资源" 的统一规范);
  • 具体策略:DefaultResourceLoader(默认资源加载)、FileSystemResourceLoader(文件系统加载)、ClassPathXmlApplicationContext(类路径加载)等;
  • 核心价值:不同资源(类路径、文件系统、URL)的加载逻辑封装为独立策略,可灵活切换且不影响调用方。
  • 以下选取ResourceLoader 、FileSystemResourceLoader核心代码片段,展示策略模式的落地逻辑:

java 复制代码
package org.springframework.core.io;
import org.springframework.lang.Nullable;
/**
 * 策略接口:定义资源加载的统一规范(策略模式核心接口)
 */
public interface ResourceLoader {
    // 类路径资源前缀(常量,子系统细节)
    String CLASSPATH_URL_PREFIX = "classpath:";
    /**
     * 策略核心方法:根据资源路径加载Resource(所有具体策略需实现此方法)
     * @param location 资源路径(如classpath:application.xml、file:/data/config.xml)
     * @return 封装后的Resource对象
     */
    Resource getResource(String location);
    /**
     * 辅助方法:获取类加载器(策略实现时依赖)
     */
    @Nullable
    ClassLoader getClassLoader();
}
arduino 复制代码
package org.springframework.core.io;
/**
 * 具体策略:文件系统资源加载器(覆盖扩展点实现文件系统加载)
 */
public class FileSystemResourceLoader extends DefaultResourceLoader {
    /**
     * 覆盖策略扩展点:实现文件系统路径加载
     */
    @Override
    protected Resource getResourceByPath(String path) {
        // 若路径为绝对路径,直接创建FileSystemResource
        if (path.startsWith("/")) {
            return new FileSystemResource(path);
        }
        // 否则创建文件系统上下文资源(支持相对路径)
        else {
            return new FileSystemContextResource(path);
        }
    }
    /**
     * 内部类:文件系统上下文资源(策略辅助实现)
     */
    private static class FileSystemContextResource extends FileSystemResource {
        public FileSystemContextResource(String path) {
            super(path);
        }
        // xxx
    }
}
角色 类 / 接口 作用
策略接口 ResourceLoader 定义getResource统一加载规范,屏蔽不同资源加载的细节
抽象策略 DefaultResourceLoader 实现通用加载逻辑(类路径、URL),提供扩展点getResourceByPath
具体策略 FileSystemResourceLoader 覆盖扩展点,实现文件系统资源加载的专属逻辑
调用方 ApplicationContext(如ClassPathXmlApplicationContext) 依赖ResourceLoader接口,无需关注具体加载策略,可灵活切换

三、实战

背景

除了大家熟悉的"出价还价"列表外,现在订单列表、"想要"收藏列表等场景也能看到心仪商品的还价信息------还价功能,在用户体验上逐步从单一场景向多场景持续演进。

1.0 版本:

在功能初期,我们采用轻量级的设计思路:

  • 聚焦核心场景:仅在还价列表页提供精简高效的还价服务
  • 极简技术实现:通过线性调用商品/库存/订单等等服务,确保功能稳定交付
  • 智能引导策略:内置还价优先级算法,帮助用户快速决策

2.0 版本:

但随着得物还价功能不断加强,系统面临了一些烦恼:

  • 场景维度:订单列表、想要<收藏>列表等新场景接入
  • 流量维度:部分页面的访问量呈指数级增长,峰值较初期上升明显

我们发现原有设计逐渐显现出一些局限性:

  • 用户体验优化:随着用户规模快速增长,如何在高并发场景下依然保持丝滑流畅的还价体验,成为重要关注点
  • 迭代效率:每次新增业务场景都需要重复开发相似逻辑
  • 协作效率:功能迭代的沟通和对接成本增加

改造点

针对上述问题,我们采用策略模式进行代码结构升级,核心改造点包括:

抽象策略接口

java 复制代码
public interface xxxQueryStrategy {
    /**
     * 策略类型
     *
     * @return 策略类型
     */
    String matchType();
    /**
     * 前置校验
     *
     * @param ctx xxx上下文
     * @return true-校验通过;false-校验未通过
     */
    boolean beforeProcess(xxxCtx ctx);
    /**
     * 执行策略
     *
     * @param ctx xxx上下文
     * @return xxxdto
     */
    xxxQueryDTO handle(xxxtx ctx);
    /**
     * 后置处理
     *
     * @param ctx xxx上下文
     */
    void afterProcess(xxxCtx ctx);
}

抽象基类 :封装公共数据查询逻辑

java 复制代码
@Slf4j
@Component
public abstract class AbstractxxxStrategy {
        /**
         * 执行策略
         *
         * @param ctx xxx上下文
         */
        public void doHandler(xxxCtx ctx) {
            // 初始化xxx数据
            initxxx(ctx);
            // 异步查询相关信息
            supplyAsync(ctx);
            // 初始化xxx上下文
            initxxxCtx(ctx);
            // 查询xxxx策略
            queryxxxGuide(ctx);
            // 查询xxx底部策略
            queryxxxBottomGuide(ctx);
        }
        /**
         * 初始化xxx数据
         *
         * @param ctx xxx上下文
         */
        protected abstract void initxxx(xxxCtx ctx);




        /**
         * 异步查询相关信息
         *
         * @param ctx xxx上下文
         */
        protected abstract void supplyAsync(xxxCtx ctx);


        /**
         * 初始化xxx上下文
         *
         * @param ctx xxx上下文
         */
        protected abstract void initxxxCtx(xxxCtx ctx);


        /**
         * 查询xxx策略
         *
         * @param ctx xxx上下文
         */
        protected abstract void queryxxxGuide(xxxCtx ctx);


        /**
         * 查询xxx底部策略
         *
         * @param ctx xxx上下文
         */
        protected abstract void queryxxxBottomGuide(xxxCtx ctx);


        /**
         * 构建出参
         *
         * @param ctx xxx上下文
         */
        protected abstract void buildXXX(xxxCtx ctx);
}

具体策略 :实现场景特有逻辑

less 复制代码
public class xxxStrategy extends AbstractxxxxStrategy implements xxxStrategy {
    /**
     * 策略类型
     *
     * @return 策略类型
     */
    @Override
    public String matchType() {
        // XXX
    }


    /**
     * 前置校验
     *
     * @param ctx xxx上下文
     * @return true-校验通过;false-校验未通过
     */
    @Override
    public boolean beforeProcess(xxxCtx ctx) {
        // XXX
    }


    /**
     * 执行策略
     *
     * @param ctx  xxx上下文
     * @return 公共出参
     */
    @Override
    public BuyerBiddingQueryDTO handle(xxxCtx ctx) {
        super.doHandler(ctx);
        // XXX
    }


    /**
     * 后置处理
     *
     * @param ctx xxx上下文
     */
    @Override
    public void afterProcess(xxxCtx ctx) {
       // XXX
    }


    /**
     * 初始化xxx数据
     *
     * @param ctx xxx上下文
     */
    @Override
    protected void initxxx(xxxCtx ctx) {
        // XXX
    }


    /**
     * 异步查询相关信息
     *
     * @param ctx  XXX上下文
     */
    @Override
    protected void supplyAsync(xxxCtx ctx) {
        // 前置异步查询
        super.preBatchAsyncxxx(ctx);
        // 策略定制业务
        // XXX
    }


    /**
     * 初始化XXX上下文
     *
     * @param ctx XXX上下文
     */
    @Override
    protected void initGuideCtx(xxxCtx ctx) {
        // XXX
    }


    /**
     * 查询XXX策略
     *
     * @param ctx XXX上下文
     */
    @Override
    protected void queryXXXGuide(xxxCtx ctx) {
        // XXX
    }


    /**
     * 查询XXX策略
     *
     * @param ctx XXX上下文
     */
    @Override
    protected void queryXXXBottomGuide(XXXCtx ctx) {
        // XXX
    }


    /**
     * 构建出参
     *
     * @param ctx XXX上下文
     */
    @Override
    protected void buildXXX(XXXCtx ctx) {
        // XXX
    }
}

运行时策略路由

typescript 复制代码
@Component
@RequiredArgsConstructor
public class xxxStrategyFactory {
    private final List<xxxStrategy> xxxStrategyList;


    private final Map<String, xxxStrategy> strategyMap = new HashMap<>();


    @PostConstruct
    public void init() {
        CollectionUtils.emptyIfNull(xxxStrategyList)
                .stream()
                .filter(Objects::nonNull)
                .forEach(strategy -> strategyMap.put(strategy.matchType(), strategy));
    }


    public xxxStrategy select(String scene) {
        return strategyMap.get(scene); 
    }
}

升级收益

1.性能提升 :

  • 同步调用改为CompletableFuture异步编排
  • 并行化独立IO操作,降低整体响应时间

2.扩展性增强 :

  • 新增场景只需实现新的Strategy类
  • 符合开闭原则(对扩展开放,对修改关闭)

3.可维护性改善 :

  • 业务逻辑按场景垂直拆分
  • 公共逻辑下沉到抽象基类
  • 消除复杂的条件分支判断

4.架构清晰度 :

  • 明确的策略接口定义
  • 各策略实现类职责单一

这种架构改造体现了组合优于继承 、面向接口编程等设计原则,通过策略模式将原本复杂的单体式结构拆分为可插拔的组件,为后续业务迭代提供了良好的扩展基础。

四、总结

在软件开发中,设计模式是一种解决特定场景问题的通用方法论,旨在提升代码的可读性、可维护性和可复用性。其核心优势在于清晰的职责分离理念、灵活的行为抽象能力以及对系统结构的优化设计。结合丰富的实践经验,设计模式已经成为开发者应对复杂业务需求、构建高质量软件系统的重要指导原则。

本文通过解析一些经典设计模式的原理、框架应用与实战案例,深入探讨了设计模式在实际开发中的价值与作用。作为代码优化的工具,更作为一种开发哲学,设计模式以简洁优雅的方式解决复杂问题,推动系统的高效与稳健。

当然了,在实际的软件开发中,我们应根据实际需求合理选择和应用设计模式,避免过度设计,同时深入理解其背后的理念,最终实现更加高效、健壮的代码与系统架构。

往期回顾

1.从0到1搭建一个智能分析OBS埋点数据的AI Agent|得物技术

2.数据库AI方向探索-MCP原理解析&DB方向实战|得物技术

3.项目性能优化实践:深入FMP算法原理探索|得物技术

4.Dragonboat统一存储LogDB实现分析|得物技术

5.从数字到版面:得物数据产品里数字格式化的那些事

文 /忘川

关注得物技术,每周更新技术干货

要是觉得文章对你有帮助的话,欢迎评论转发点赞~

未经得物技术许可严禁转载,否则依法追究法律责任。

相关推荐
阿拉斯攀登5 小时前
ThreadLocal 全解析(Spring Boot 实战篇)
java·spring boot·threadlocal
BBB努力学习程序设计5 小时前
Java模块化系统深度解析:从JAR地狱到JPMS模块化
java
dddaidai1236 小时前
深入JVM(三):JVM执行引擎
java·jvm
Hui Baby6 小时前
saga文件使用
java
墨夶6 小时前
交易所安全保卫战:从冷钱包到零知识证明,让黑客连边都摸不着!
java·安全·区块链·零知识证明
山风wind6 小时前
Tomcat三步搭建局域网文件共享
java·tomcat
a努力。6 小时前
网易Java面试被问:偏向锁在什么场景下反而降低性能?如何关闭?
java·开发语言·后端·面试·架构·c#
小新1106 小时前
Spring boot 之 Hello World 番外:如何修改端口号
java·spring boot·后端
百花~6 小时前
Spring Boot 日志~
java·spring boot·后端