Java经典23种设计模式全面详解

在Java软件开发领域,设计模式是前人总结的、可复用的软件设计最佳实践,是解决代码耦合、冗余、扩展性差等常见问题的标准化方案。目前行业通用、教科书及面试公认的标准是GoF(四人帮)23种经典设计模式,出自《Design Patterns: Elements of Reusable Object‑Oriented Software》一书,并非Java专属,而是适配所有面向对象编程语言,也是Java开发工程师必备的核心编程思想。

设计模式的核心价值在于解耦代码、复用逻辑、规范结构、提升扩展性,帮助开发者摆脱杂乱的硬编码,写出高内聚、低耦合、易维护、可拓展的高质量代码。根据核心解决的问题不同,23种设计模式可划分为三大核心类别:创建型模式、结构型模式、行为型模式,下文将逐一详解。

一、创建型模式(5种):管控对象创建,解耦创建与使用

创建型模式的核心作用是隐藏对象实例化的复杂细节,将对象的创建逻辑与业务使用逻辑分离,灵活控制对象的创建数量、创建方式,避免重复创建、资源浪费,适配各类对象初始化场景。

1. 单例模式(Singleton)

最常用的设计模式之一,核心是保证一个类在整个程序运行过程中仅有一个实例对象,并提供全局统一的访问入口。可分为饿汉式、懒汉式、双重检查锁、枚举单例等多种实现方式,适配不同线程安全场景。

使用方法:私有化构造方法避免外部new对象,通过静态变量或静态方法维护唯一实例,对外提供统一静态获取实例接口;优先使用枚举单例,天然线程安全、防止反射破坏。

适用场景:配置类、工具类、线程池、缓存对象等无需重复创建的全局资源。

核心优点:全局唯一实例,减少对象创建开销、节省内存;统一访问入口,方便全局资源管控;避免多实例造成的资源冲突、数据不一致问题。

缺点与注意坑:违背单一职责原则,类同时负责自身业务和实例创建;普通懒汉式存在线程安全问题;非枚举单例可被反射、序列化破坏单例;扩展困难,无法动态生成多实例。

源码案例:JDK Runtime类、Spring容器默认单例Bean、枚举单例最佳实践。

极简代码样例(枚举单例)

// 枚举单例(最优、线程安全、防反射)
public enum Singleton {
    INSTANCE;
    public void doSomething(){
        System.out.println("单例业务逻辑");
    }
}
// 调用:Singleton.INSTANCE.doSomething();

2. 工厂方法模式(Factory Method)

定义创建对象的通用接口,将具体的对象实例化逻辑延迟到子类实现,核心是一个工厂对应一种产品,遵循开闭原则,新增产品时无需修改原有代码。

使用方法:创建抽象工厂接口/抽象类,定义产品创建抽象方法;为每一种具体产品创建对应的工厂子类,重写创建方法生成对应对象;业务层通过抽象工厂调用创建方法,无需关心具体实现类。

适用场景:同类产品批量创建、需要灵活拓展产品类型的场景,如支付方式、日志类型创建。

核心优点:完全遵循开闭原则,新增产品只需新增工厂类,无需改原有代码;将创建逻辑与业务逻辑解耦;统一产品创建规范,代码结构清晰。

缺点与注意坑:每新增一个产品,必须新增对应工厂类,造成类数量激增;仅支持单一产品创建,无法适配多系列关联产品;增加系统抽象层级,提高入门理解成本。

源码案例:JDK Calendar工厂类、LoggerFactory日志工厂、Spring Bean工厂方法。

极简代码样例(工厂方法)

// 产品接口
interface Product{}
class Phone implements Product{}

// 抽象工厂
interface Factory{
    Product create();
}

// 具体工厂
class PhoneFactory implements Factory{
    @Override
    public Product create() {
        return new Phone();
    }
}

3. 抽象工厂模式(Abstract Factory)

工厂方法模式的升级版本,核心是一个超级工厂创建多个产品工厂,可以生产一系列相关联的产品族群,解决多品类、多维度对象创建问题。

使用方法:定义顶层抽象工厂,包含多个不同产品族的创建方法;定义多个产品抽象接口,再实现不同系列的具体产品;具体工厂实现顶层抽象工厂,负责生产同一系列的所有关联产品;业务端通过超级工厂获取整套产品对象。

适用场景:多系列关联对象统一创建,如操作系统适配(Windows/Mac 按钮、窗口、弹窗整套组件)。

核心优点:约束产品族关联关系,保证同系列产品配套使用;隔离不同产品系列,拓展性极强;一次性创建整套关联对象,避免产品搭配混乱。

缺点与注意坑:产品族一旦固定难以修改,新增全新产品等级需修改顶层抽象工厂;类体系极其庞大,代码冗余度高;学习和维护成本远高于工厂方法模式。

源码案例:JDK javax.xml.parsers文档解析工厂、跨平台UI组件工厂设计。

极简代码样例(抽象工厂)

// 产品族接口
interface Button{}
interface Window{}

// 抽象工厂(生产整套产品)
interface SystemFactory{
    Button createButton();
    Window createWindow();
}

4. 建造者模式(Builder)

将复杂对象的构建过程与具体实现分离,可以通过分步组装、灵活配置参数,创建不同属性的复杂对象,有效解决多参数构造器冗余、参数混乱的问题。

使用方法:在目标类内部静态嵌套Builder建造者类;Builder类拥有与目标类一致的属性,提供链式setter方法并返回自身;提供build()方法完成对象构建;业务端通过类名.builder().参数().build()链式调用创建对象。

适用场景:参数繁多、对象结构复杂的场景,如实体类、HTTP请求参数、复杂配置对象构建。

核心优点:解决多参数构造器参数混乱、重叠问题;链式调用代码简洁优雅,可读性极强;分步构建对象,可灵活控制参数组合;对象构建与业务解耦。

缺点与注意坑:需要额外编写Builder内部类,增加代码量;仅适合复杂对象,简单对象使用会过度设计;需手动同步实体类与Builder属性,易出现字段遗漏。

源码案例:Spring RestTemplateBuilder、JDK StringBuilder、Lombok @Builder注解底层实现。

极简代码样例(建造者)

public class User {
    private String name;
    private int age;

    // 私有构造
    private User(Builder builder){
        this.name = builder.name;
        this.age = builder.age;
    }

    public static Builder builder(){
        return new Builder();
    }

    public static class Builder{
        private String name;
        private int age;
        public Builder name(String name){
            this.name = name;
            return this;
        }
        public Builder age(int age){
            this.age = age;
            return this;
        }
        public User build(){
            return new User(this);
        }
    }
}
// 调用:User user = User.builder().name("张三").age(18).build();

5. 原型模式(Prototype)

通过复制已有原型对象的方式创建新对象,无需重复执行复杂的初始化逻辑,分为浅克隆和深克隆,大幅提升复杂对象的创建效率。

使用方法:目标类实现Cloneable接口,重写clone()方法;浅克隆直接调用super.clone(),深克隆通过序列化、递归复制引用对象实现;通过已有对象调用clone方法快速生成新对象,无需new初始化。

适用场景:对象创建成本高、重复创建频繁的场景,如系统配置、模板对象、大数据对象复制。

核心优点:跳过复杂初始化流程,大幅提升大对象创建效率;快速复制对象,代码复用性高;保留对象原有状态,适配模板化场景。

缺点与注意坑:浅克隆仅复制引用,修改新对象会影响原对象;深克隆实现复杂,层级过深易出现循环引用;需重写clone方法,破坏封装性。

源码案例:JDK Object.clone()、ArrayList克隆、Spring原型Bean对象复制。

极简代码样例(原型模式)

public class Prototype implements Cloneable{
    private String name;

    // 浅克隆
    @Override
    public Prototype clone() throws CloneNotSupportedException {
        return (Prototype)super.clone();
    }
}

二、结构型模式(7种):优化类与对象组合,重构代码结构

结构型模式聚焦类和对象的组合方式,通过拼接、适配、装饰、代理等方式优化代码结构,整合现有资源、拓展对象功能,无需修改原有核心代码,提升系统的灵活性和兼容性。

1. 适配器模式(Adapter)

充当“转换桥梁”,将现有类的接口转换成客户端所需的目标接口,解决接口不兼容、新旧代码适配问题,无需修改原有源码即可实现功能对接。分为类适配器、对象适配器、接口适配器三种类型。

使用方法:定义客户端需要的目标接口;持有原接口/原类的对象(对象适配器)或继承原类(类适配器);适配器类实现目标接口,内部调用原类的方法并做参数、逻辑适配转换;业务端只调用目标接口即可兼容旧代码。

适用场景:第三方接口适配、新旧系统对接、老旧代码兼容新业务逻辑。

核心优点:无需修改原有源码,完美兼容老旧代码;解决接口不兼容问题,实现系统平滑对接;职责单一,适配逻辑集中,便于维护。

缺点与注意坑:过度使用会增加系统复杂度;类适配器受限于Java单继承,通用性差;适配逻辑过多会导致适配器类臃肿。

源码案例:Spring MVC HandlerAdapter、JDK InputStreamReader、第三方SDK接口适配层。

极简代码样例(适配器)

// 原有旧接口
class OldService{
    public void oldFunc(){ System.out.println("旧功能"); }
}
// 目标新接口
interface NewService{
    void newFunc();
}
// 适配器
class Adapter implements NewService{
    private OldService old = new OldService();
    @Override
    public void newFunc() {
        old.oldFunc(); // 适配转换
    }
}

2. 桥接模式(Bridge)

将抽象层与实现层解耦,让两者可以独立拓展,用组合关系替代继承关系,避免多层继承导致的类爆炸问题,适配多维度可变的业务场景。

使用方法:拆分系统两个独立变化维度,分别定义抽象层和实现层;抽象层持有实现层的引用(组合);两端各自独立拓展子类,自由组合搭配,无需新增大量子类。

适用场景:多维度独立变化的场景,如手机品牌+手机功能、图形形状+图形颜色。

核心优点:彻底解决继承导致的类爆炸问题;多维度完全独立拓展,互不影响;耦合度极低,拓展性远超继承方案。

缺点与注意坑:需要拆分维度、抽象层级,设计难度高;增加系统抽象层数,代码理解成本提升;仅适用于多维度变化场景,单一变化场景属于过度设计。

源码案例:JDBC驱动适配、图形绘制系统、消息推送多渠道适配。

极简代码样例(桥接模式)

// 实现层(渠道)
interface MsgChannel{ void send(); }
class SmsChannel implements MsgChannel{ public void send(){} }

// 抽象层(消息)持有实现层
abstract class Message{
    protected MsgChannel channel;
    public Message(MsgChannel channel){ this.channel = channel; }
    public abstract void push();
}

3. 组合模式(Composite)

统一处理单个对象和对象组合,将对象构建成树形层级结构,让客户端可以一致访问叶子节点和容器节点,忽略层级差异。

使用方法:定义统一抽象构件类,声明增删子节点、遍历等通用方法;创建容器构件(树枝)和叶子构件(叶子);容器构件内部维护子节点集合,实现节点增删、遍历逻辑;客户端统一操作抽象构件,自动适配单节点和树形结构。

适用场景:树形结构业务,如系统菜单、部门组织架构、文件目录层级。

核心优点:统一叶子节点和树枝节点操作,客户端无需区分层级;完美适配树形结构,代码简洁通用;支持灵活组装树形结构,拓展性强。

缺点与注意坑:违背单一职责,容器节点兼具存储和业务逻辑;部分节点无意义方法需空实现,存在接口冗余;树形层级过深时遍历效率偏低。

源码案例:Java Swing组件树、系统权限菜单、文件系统File结构。

极简代码样例(组合模式)

// 抽象构件
interface Menu{ void show(); }
// 叶子节点
class LeafMenu implements Menu{
    public void show(){ System.out.println("子菜单"); }
}
// 容器节点
class RootMenu implements Menu{
    private List<Menu> list = new ArrayList<>();
    public void add(Menu m){ list.add(m); }
    public void show(){ list.forEach(Menu::show); }
}

4. 装饰器模式(Decorator)

在不修改原有对象结构的前提下,动态为对象叠加额外功能,支持多层装饰、灵活拓展功能,遵循开闭原则。

使用方法:定义统一组件抽象接口;实现基础原始组件(核心功能);创建装饰器抽象类继承组件接口,持有组件对象;具体装饰器继承装饰器类,重写方法实现功能增强;业务端嵌套组装装饰器,实现动态叠加功能。

适用场景:功能动态叠加场景,如Java IO流、权限拓展、日志增强、数据加密。

核心优点:完美遵循开闭原则,动态叠加功能无需修改原代码;多层嵌套装饰,灵活组合功能;职责拆分清晰,核心功能与拓展功能解耦。

缺点与注意坑:多层装饰嵌套后调试困难;会产生大量相似装饰类,类数量增多;装饰层级顺序会影响最终执行效果。

源码案例:JDK IO流(BufferedReader装饰Reader)、Spring Cache装饰器、权限校验增强。

极简代码样例(装饰器)

// 组件接口
interface Drink{ void drink(); }
// 基础组件
class Water implements Drink{ public void drink(){} }
// 装饰器
class SugarDecorator implements Drink{
    private Drink drink;
    public SugarDecorator(Drink d){ this.drink = d; }
    public void drink() {
        drink.drink();
        System.out.println("加糖增强");
    }
}

5. 外观模式(Facade)

为多个复杂的子系统提供一个统一的高层入口,屏蔽底层复杂逻辑,简化客户端调用流程,降低系统耦合度。

使用方法:梳理多个底层复杂子系统,保留原有内部逻辑;创建外观门面类,封装所有子系统的调用逻辑;对外提供统一简洁的业务方法,客户端仅调用门面类,无需直接操作底层子系统。

适用场景:复杂子系统整合调用,如项目初始化、接口统一封装、第三方服务聚合调用。

核心优点:屏蔽底层复杂逻辑,简化客户端调用;降低客户端与子系统的耦合度;统一入口,规范接口调用流程。

缺点与注意坑:容易形成上帝类,门面类逻辑过于臃肿;新增子系统需修改门面类,违背开闭原则;过度封装会隐藏底层细节,不利于问题排查。

源码案例:Spring ApplicationContext、Tomcat启动门面、第三方服务聚合工具类。

极简代码样例(外观模式)

// 多个子系统
class SubSystemA{ void a(){} }
class SubSystemB{ void b(){} }
// 门面统一入口
class Facade{
    private SubSystemA a = new SubSystemA();
    private SubSystemB b = new SubSystemB();
    public void doAll(){
        a.a();
        b.b();
    }
}

6. 享元模式(Flyweight)

通过共享已有对象,减少重复对象创建,复用内存资源,大幅降低系统内存占用,区分对象的内部不变状态和外部可变状态。

使用方法:抽象享元类,定义内部不变属性和对外业务方法;实现具体享元类,固化内部状态;创建享元工厂,通过容器缓存已有对象;客户端请求对象时,工厂优先从缓存获取,不存在则新建并缓存。

适用场景:大量相似对象复用场景,如线程池、字符串常量池、连接池、棋盘棋子对象。

核心优点:极大减少重复对象创建,节省内存资源;提升对象获取效率,避免频繁GC;统一管理共享对象,资源利用率极高。

缺点与注意坑:需要区分内部不变状态和外部可变状态,设计复杂;共享对象线程安全管控难度大;为了复用会牺牲部分个性化能力。

源码案例:JDK String常量池、Integer缓存、线程池ThreadPoolExecutor、数据库连接池。

极简代码样例(享元模式)

// 享元工厂缓存对象
class FlyweightFactory{
    private Map<String, Flyweight> cache = new HashMap<>();
    public Flyweight get(String key){
        if(!cache.containsKey(key)){
            cache.put(key,new ConcreteFlyweight());
        }
        return cache.get(key);
    }
}
interface Flyweight{}
class ConcreteFlyweight implements Flyweight{}

7. 代理模式(Proxy)

通过代理对象替代原始对象执行操作,可在不修改原始代码的前提下,实现前置增强、后置处理、权限控制、延迟加载等功能,分为静态代理和动态代理(JDK动态代理、CGLIB代理)。

使用方法:静态代理:实现与目标对象相同的接口,持有目标对象,方法前后做增强;JDK动态代理:实现InvocationHandler,重写invoke方法做增强,通过Proxy生成代理对象;CGLIB代理:通过继承目标类生成子类代理,无需接口,适配无接口类增强。

适用场景:Spring AOP、事务控制、权限校验、远程调用、缓存代理。

核心优点:业务与增强逻辑完全解耦,无侵入拓展功能;支持前置、后置、异常、最终增强;动态代理无需编写大量静态代理类,灵活性极高。

缺点与注意坑:JDK动态代理仅支持接口代理,无接口类需用CGLIB;代理会增加调用链路,产生微小性能损耗;多层代理嵌套排查问题困难。

源码案例:Spring AOP动态代理、Mybatis Mapper代理、RPC远程调用代理。

极简代码样例(静态代理)

interface UserService{ void save(); }
class UserServiceImpl implements UserService{ public void save(){} }

// 代理类
class UserProxy implements UserService{
    private UserService target;
    public UserProxy(UserService t){ target = t; }
    @Override
    public void save() {
        System.out.println("前置增强");
        target.save();
        System.out.println("后置增强");
    }
}

三、行为型模式(11种):规范对象交互,拆分业务职责

行为型模式是开发中应用最广泛的一类模式,核心是规范对象之间的交互逻辑、拆分业务职责、优化流程调度,解决代码逻辑混乱、职责模糊、流程固化等问题。

1. 责任链模式(Chain of Responsibility)

将多个处理节点串联成一条链式结构,请求沿着链条依次传递,各节点各司其职、独立处理,可灵活增删节点、调整处理顺序,实现请求与处理解耦。

使用方法:定义抽象处理者,持有下一个处理节点引用,定义处理请求方法;创建多个具体处理者,各自实现对应业务逻辑,满足条件则处理,不满足则传递给下一级;客户端组装链条顺序,统一发起请求。

适用场景:多级审批、过滤器链、拦截器、日志级别处理、异常逐级捕获。

核心优点:请求与处理节点解耦,灵活增删、调整节点顺序;各节点职责单一,各司其职;无需修改原有逻辑即可新增处理流程。

缺点与注意坑:请求可能未被任何节点处理,造成请求丢失;链条过长会影响执行效率;需手动处理链条传递逻辑,避免死循环。

源码案例:Spring MVC拦截器链、Servlet过滤器链、Dubbo过滤器、审批流系统。

极简代码样例(责任链)

abstract class Handler{
    protected Handler next;
    public void setNext(Handler next){ this.next = next; }
    public abstract void handle(int num);
}

class FirstHandler extends Handler{
    public void handle(int num){
        if(num <10) return;
        if(next!=null) next.handle(num);
    }
}

2. 命令模式(Command)

将客户端的请求封装为独立的命令对象,实现请求发送者与执行者解耦,支持命令排队、撤销、重试、批量执行等拓展功能。

使用方法:定义命令抽象接口,包含execute()、undo()等方法;创建具体命令类,绑定接收者执行者,实现执行、撤销逻辑;调用者持有命令对象,统一触发执行;客户端组装命令、提交给调用者,实现请求批量管理。

适用场景:订单操作、任务调度、快捷键指令、可撤销/恢复的业务操作。

核心优点:请求封装为对象,可排队、缓存、撤销、重试;请求发送者与执行者完全解耦;批量管理请求,拓展性强。

缺点与注意坑:每一种指令都需新建命令类,类数量激增;简单指令场景使用属于过度设计;需额外维护命令队列、撤销栈,增加复杂度。

源码案例:Swing按钮事件、任务调度队列、Redis事务指令、订单操作日志。

极简代码样例(命令模式)

// 命令接口
interface Command{ void execute(); }
// 接收者
class Receiver{ void action(){} }
// 具体命令
class WorkCommand implements Command{
    private Receiver r;
    public void execute(){ r.action(); }
}

3. 解释器模式(Interpreter)

定义语言文法规则,构建解释器用于解析自定义表达式、语法,将复杂的语法解析逻辑标准化。

使用方法:定义抽象表达式解释器接口,实现interpret()解析方法;拆分终结符表达式、非终结符表达式,分别解析基础元素和组合语法;构建语法树组装各类表达式;客户端传入上下文数据,通过语法树完成表达式解析运算。

适用场景:规则引擎、表达式计算、SQL解析、自定义脚本解析。

核心优点:将复杂语法解析标准化、结构化;易于拓展新的语法规则;语法与业务逻辑解耦,通用性强。

缺点与注意坑:语法复杂时类体系极其庞大;维护和学习成本极高;日常业务极少使用,适用场景极窄。

源码案例:JDK正则表达式、SpEL表达式解析、SQL语法解析器、规则引擎表达式。

极简代码样例(解释器)

// 抽象表达式
interface Expression{ int interpret(); }
// 终结符表达式
class NumExpression implements Expression{
    private int num;
    public int interpret(){ return num; }
}

4. 迭代器模式(Iterator)

提供统一的遍历接口,屏蔽集合底层存储结构差异,让客户端可以统一遍历各类集合对象,无需关注底层实现。Java集合框架的Iterator就是典型实现。

使用方法:定义迭代器抽象接口,包含hasNext()、next()等遍历方法;聚合集合类提供获取迭代器的方法;具体迭代器实现遍历逻辑,维护遍历游标;客户端通过统一迭代器接口遍历,无需关注数组、链表等底层结构。

适用场景:各类集合数据遍历、自定义数据结构迭代查询。

核心优点:统一遍历接口,屏蔽底层存储差异;遍历与集合结构解耦;支持自定义迭代规则,通用性强。

缺点与注意坑:对于简单集合遍历,迭代器略显繁琐;并发遍历存在快速失败机制,易触发异常;无法直接获取集合索引。

源码案例:JDK Iterator迭代器、List/Set遍历、Spring集合工具迭代。

极简代码样例(迭代器)

// JDK原生迭代器典型用法
List<String> list = new ArrayList<>();
Iterator<String> it = list.iterator();
while(it.hasNext()){
    String s = it.next();
}

5. 中介者模式(Mediator)

引入中介对象,解耦多个对象之间的直接交互,所有对象的通信都通过中介转发,避免多对象网状耦合,简化系统交互逻辑。

使用方法:定义中介者抽象接口,声明消息转发方法;实现具体中介者,维护所有交互同事对象;定义同事抽象类,持有中介者引用;各同事对象不直接通信,所有消息通过中介者转发调度。

适用场景:多模块交互、聊天室、消息中间件、设备联动控制。

核心优点:将多对多网状耦合转为一对多星型耦合;大幅简化对象交互逻辑;新增交互对象无需修改原有代码。

缺点与注意坑:中介者会成为核心枢纽,逻辑过度集中;中介者复杂度极高,维护难度大;单一故障会导致所有交互失效。

源码案例:MQ消息中间件、聊天室服务端、Spring事件分发器、网关路由。

极简代码样例(中介者)

// 中介者
interface Mediator{ void send(String msg, User user); }
// 同事类
abstract class User{
    protected Mediator mediator;
    public User(Mediator m){ mediator = m; }
}

6. 备忘录模式(Memento)

在不破坏封装性的前提下,捕获对象的内部状态并保存,支持对象状态回滚、恢复,实现数据快照保存。

使用方法:创建备忘录类,专门存储源对象的状态数据;源对象提供创建备忘录、恢复备忘录的方法;管理者类负责存储、获取多个备忘录快照;业务需要回滚时,通过管理者取出历史快照恢复对象状态。

适用场景:文档撤销恢复、游戏存档、订单状态回溯、数据版本备份。

核心优点:不破坏对象封装性,安全保存内部状态;支持数据快照回滚,适配撤销场景;状态保存与业务对象解耦。

缺点与注意坑:频繁保存快照会占用大量内存;状态复杂时备忘录类冗余;需手动管理快照生命周期,易造成内存泄漏。

源码案例:编辑器撤销操作、游戏存档功能、订单状态版本回溯、Redis快照备份。

极简代码样例(备忘录)

// 备忘录
class Memento{ String state; }
// 源对象
class Origin{
    private String state;
    public Memento save(){ return new Memento(); }
    public void restore(Memento m){}
}

7. 观察者模式(Observer)

经典的发布-订阅模式,定义一对多的依赖关系,当目标对象状态更新时,自动通知所有订阅者,实现事件的异步、松耦合通知。

使用方法:定义抽象观察者和抽象被观察者;被观察者维护观察者集合,提供订阅、取消订阅、通知方法;具体被观察者状态变化时,触发批量通知;所有已订阅的观察者自动接收消息并执行更新逻辑。

适用场景:消息推送、事件监听、Spring事件机制、MQ消息订阅、前端数据更新联动。

核心优点:发布者与订阅者完全解耦,异步通知;一对多联动,拓展订阅者无需改源码;事件驱动,响应灵活。

缺点与注意坑:订阅者过多会导致通知链路过长;异步场景异常排查困难;订阅顺序不可控,易出现业务时序问题。

源码案例:Spring事件监听、JDK Observer、MQ发布订阅、Nacos配置监听。

极简代码样例(观察者)

// 观察者
interface Observer{ void update(); }
// 被观察者
class Subject{
    private List<Observer> list = new ArrayList<>();
    public void add(Observer o){ list.add(o); }
    public void notifyAllObservers(){
        list.forEach(Observer::update);
    }
}

8. 状态模式(State)

将对象的不同状态及状态对应的行为拆分,状态驱动业务逻辑,避免大量if-else、switch判断,让状态切换更灵活。

使用方法:抽取所有状态,定义统一状态抽象类/接口,声明状态对应行为方法;为每种状态创建独立状态类,实现专属业务逻辑;上下文对象持有当前状态,提供状态切换方法;业务执行时自动根据当前状态调用对应逻辑。

适用场景:订单状态、支付状态、审批状态、设备启停状态机场景。

核心优点:彻底消除大量if-else、switch判断;状态与行为绑定,逻辑清晰;新增状态无需修改原有代码,符合开闭原则。

缺点与注意坑:状态过多会产生大量状态类;状态切换逻辑分散,整体流程梳理困难;简单状态场景使用属于过度设计。

源码案例:订单状态机、支付流程状态、工作流审批、设备控制状态切换。

极简代码样例(状态模式)

// 状态接口
interface State{ void handle(); }
// 具体状态
class PaySuccessState implements State{
    public void handle(){ System.out.println("支付成功逻辑"); }
}
// 上下文
class Context{
    private State state;
    public void setState(State s){ state = s; }
    public void doWork(){ state.handle(); }
}

9. 策略模式(Strategy)

定义一系列独立的算法策略,将每个算法封装,可动态替换、自由切换算法,彻底消除冗余的条件判断语句。

使用方法:定义策略抽象接口,声明算法执行方法;创建多个具体策略类,各自实现不同算法逻辑;上下文类持有策略接口,提供策略切换方法;业务端根据场景动态注入对应策略,执行算法。

适用场景:支付方式选择、折扣算法、排序策略、路由策略、权限校验规则切换。

核心优点:算法与业务解耦,动态切换策略;消除冗余条件判断,代码简洁;新增算法无需改源码,拓展性极强。

缺点与注意坑:策略过多会产生大量策略类;客户端需理解不同策略的差异,增加使用成本;简单算法场景会造成代码冗余。

源码案例:Spring资源加载策略、支付渠道策略、排序算法切换、路由负载均衡策略。

极简代码样例(策略模式)

// 策略接口
interface PayStrategy{ void pay(); }
// 具体策略
class AliPay implements PayStrategy{ public void pay(){} }
// 上下文
class PayContext{
    private PayStrategy strategy;
    public void setStrategy(PayStrategy s){ strategy = s; }
    public void execute(){ strategy.pay(); }
}

10. 模板方法模式(Template Method)

在抽象类中定义算法的固定执行骨架,将可变的具体步骤延迟到子类实现,保证流程统一的同时,支持局部逻辑自定义拓展。

使用方法:创建抽象模板类,定义final修饰的核心骨架方法,固定执行步骤;将差异化步骤定义为抽象方法或钩子方法;子类继承模板类,重写差异化步骤实现自定义逻辑;业务端调用模板骨架方法,自动执行统一流程+自定义步骤。

适用场景:统一流程模板,如文件解析、报表生成、接口请求流程、定时任务执行流程。

核心优点:统一业务执行流程,规范代码风格;固定骨架、灵活拓展细节,兼顾统一性和灵活性;避免重复编写通用流程代码。

缺点与注意坑:骨架流程固定,无法整体修改流程;子类必须实现所有抽象方法,灵活性受限;钩子方法过多会增加复杂度。

源码案例:Spring JdbcTemplate、Servlet生命周期、定时任务执行模板、文件解析通用流程。

极简代码样例(模板方法)

// 模板抽象类
abstract class Template{
    // 固定骨架
    public final void run(){
        before();
        doWork();
        after();
    }
    public void before(){}
    public abstract void doWork(); // 子类实现
    public void after(){}
}

11. 访问者模式(Visitor)

将数据结构与数据操作解耦,在不修改数据结构的前提下,动态新增访问操作,适配数据结构固定、操作频繁拓展的场景。

使用方法:定义访问者接口,声明不同数据元素的访问方法;创建具体访问者,实现各类数据的操作逻辑;数据元素抽象类提供接收访问者的方法;对象结构类存储所有元素,统一接受访问者遍历操作。

适用场景:数据统计、报表分析、DOM树遍历、复杂数据结构多维度操作。

核心优点:数据结构与数据操作完全解耦;无需修改数据结构即可新增操作;统一遍历操作规范,适配复杂树形结构。

缺点与注意坑:违背迪米特法则,访问者深度依赖元素结构;数据结构变更时需修改所有访问者;适用场景极其狭窄,日常开发极少使用。

极简代码样例(访问者)

// 访问者接口
interface Visitor{ void visit(Element e); }
// 元素接口
interface Element{ void accept(Visitor v); }
// 具体元素
class ConcreteElement implements Element{
    public void accept(Visitor v){ v.visit(this); }
}

源码案例:DOM文档遍历、编译器语法树遍历、数据报表统计、权限节点批量操作。

四、核心补充:易混淆概念与开发重点

1. 总数统计

经典GoF设计模式共计23种,5种创建型+7种结构型+11种行为型,是Java开发、面试、项目实战的标准参考。日常开发中还有空对象模式、缓存模式等拓展模式,但不属于经典23种范畴。

2. 设计模式 ≠ 架构模式

很多开发者容易混淆两类概念:设计模式是代码层级的微观设计方案,作用于类和对象;而MVC、MVVM、微服务、分层架构、DDD等属于架构模式,是系统整体的宏观设计方案,二者层级完全不同。

3. 开发高频核心模式(必掌握)

23种模式无需全部死记硬背,项目实战和面试中高频使用的核心模式仅10种左右:单例模式、工厂模式、建造者模式、代理模式、适配器模式、装饰器模式、观察者模式、策略模式、模板方法模式、责任链模式,掌握这些模式即可解决90%的代码设计问题。

五、总结

Java设计模式的本质不是固定的代码模板,而是优秀的编程思维。创建型模式解决“对象怎么建”,结构型模式解决“代码怎么组”,行为型模式解决“逻辑怎么跑”。熟练掌握23种经典设计模式,能够帮助开发者跳出基础编码思维,从架构层面优化代码,提升代码的复用性、扩展性与可维护性,是从初级程序员进阶中高级开发的核心必备能力。

Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐