Loading...

23种设计模式,一次性讲明白

设计模式其实就是前辈们写代码踩了无数坑,总结出来的代码编写最佳实践,专门用来解决特定场景下的代码复用、解耦、扩展性问题。

可以分为创建型模式、结构型模式、行为型模式三类。

分类 核心作用 包含的设计模式 数量
创建型模式 解决对象怎么创建的问题,解耦对象的创建和使用 单例、工厂方法、抽象工厂、建造者、原型 5种
结构型模式 解决类/对象怎么组合的问题,不修改原有代码实现功能扩展 适配器、桥接、组合、装饰、外观、享元、代理 7种
行为型模式 解决类/对象之间怎么交互、职责怎么分配的问题,实现灵活的通信和流程控制 模板方法、命令、迭代器、观察者、中介者、备忘录、解释器、状态、策略、职责链、访问者 11种

记忆口诀

创建型口诀(5种)

单身汉在工厂抽象建原型

一个单身汉,在工厂里用抽象方法,建造了一个原型。

结构型口诀(7种)

桥上代理卖装饰,组合适配享外观

桥上的代理商在卖装饰品,把零件组合适配好,让你享受精美外观。

行为型口诀(11种)

访客观察定策略,模板迭代改状态,备忘命令交中介,解释责任一条链。

访客观察后定下策略,按模板迭代改变状态,把备忘录和命令交给中介,出了问题沿责任链逐一解释。

创建型模式

不让你自己new对象,把对象创建的逻辑封装起来,和业务代码解耦。

1. 单例模式

整个系统里,这个类有且只有一个实例对象,不管你在哪获取,拿到的都是同一个东西。

类比

就像公司里只有一个 CEO,不管哪个部门找,对接的都是同一个人。

适用场景

Spring容器里的单例Bean、全局配置类、日志对象

代码示例

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class Singleton {
    // 1. 私有构造方法:禁止外部new对象
    private Singleton() {}

    // 2. 私有静态实例,volatile禁止指令重排
    private static volatile Singleton instance;

    // 3. 公共静态方法,获取唯一实例
    public static Singleton getInstance() {
        // 第一次校验:实例已存在就直接返回,不用抢锁
        if (instance == null) {
            // 加锁,保证线程安全
            synchronized (Singleton.class) {
                // 第二次校验:防止多线程同时进入第一个if,重复创建
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

2. 工厂方法模式

定义一个创建对象的工厂接口,让子类决定创建哪个对象,把对象的创建延迟到子类。

类比

就像奶茶店是工厂接口,蜜雪冰城、喜茶是子类工厂,你去哪个店,就喝哪个店的奶茶,不用管奶茶是怎么做的。

适用场景

  • 不知道要创建的对象具体类型,交给子类决定
  • 把对象创建和业务代码解耦
  • Spring的FactoryBean

代码示例

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
// 1. 产品接口:奶茶
public interface MilkTea {
    void make();
}

// 2. 具体产品:蜜雪冰城奶茶
public class MixueMilkTea implements MilkTea {
    @Override
    public void make() {
        System.out.println("蜜雪冰城奶茶做好了!");
    }
}

3. 抽象工厂模式

工厂方法的升级版,一个工厂可以创建一系列相关的产品,而不是只创建一个。

和工厂方法的区别是:

工厂方法是单个产品,而抽象工厂是一套相关产品。

类比

就像蜜雪冰城不仅能做奶茶,还能做冰淇淋、圣代,一个工厂搞定同品牌的一系列产品。

适用于需要创建一系列相关联的产品,而不是单个产品的场景(比如同一品牌的奶茶 + 冰淇淋 + 圣代)。

4. 建造者模式

把一个复杂对象的创建和表示分离,一步一步构建出复杂对象,不用管内部组装细节。

类比

就像组装电脑,你只需要告诉装机店你要什么 CPU、显卡、内存,装机店帮你一步步组装好,不用你自己焊主板。

适用场景

  • 复杂对象的创建,参数特别多(比如超过4个参数)
  • Lombok的@Builder注解

代码示例

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
// 电脑类(复杂对象)
public class Computer {
    private String cpu;
    private String gpu;
    private int memory;
    private int storage;

    // 私有构造方法,只能通过Builder创建
    private Computer(Builder builder) {
        this.cpu = builder.cpu;
        this.gpu = builder.gpu;
        this.memory = builder.memory;
        this.storage = builder.storage;
    }

    // 建造者静态内部类
    public static class Builder {
        private String cpu;
        private String gpu;
        private int memory;
        private int storage;

        // 链式调用设置参数
        public Builder cpu(String cpu) {
            this.cpu = cpu;
            return this;
        }

        public Builder gpu(String gpu) {
            this.gpu = gpu;
            return this;
        }

        public Builder memory(int memory) {
            this.memory = memory;
            return this;
        }

        public Builder storage(int storage) {
            this.storage = storage;
            return this;
        }

        // 构建最终对象
        public Computer build() {
            return new Computer(this);
        }
    }
}

// 测试
public class Test {
    public static void main(String[] args) {
        // 链式调用,一步一步构建对象
        Computer computer = new Computer.Builder()
                .cpu("i7-14700K")
                .gpu("RTX4070")
                .memory(32)
                .storage(1024)
                .build();
    }
}

5. 原型模式

基于一个已有的对象,克隆出一个一模一样的新对象,不用重新new和初始化。

类比

就像打印机复印文件,不用重新手写一遍,直接复印一份一模一样的。

适用场景

  • 对象创建成本很高(比如初始化要查数据库、调接口)
  • 想要和原对象一模一样的新对象,又不想重新初始化
  • Spring的Bean的原型作用域

结构型模式

不修改原有代码,通过类/对象的组合,实现功能扩展和灵活组装。

1. 适配器模式

把一个不兼容的接口,转换成你想要的兼容接口,让两个不搭边的类能一起工作。

类比

就像 Type-C 转 3.5mm 耳机转接头,手机只有 Type-C 口,耳机是 3.5mm 口,转接头让它们能一起用。

适用场景

  • 老系统接口复用,适配新的业务
  • Spring MVC的HandlerAdapter

2. 装饰器模式

在不修改原有类的基础上,动态给对象添加新功能,比继承更灵活。

类比

就像买奶茶,基础款是纯茶,你可以加珍珠、加椰果、加奶盖,一层一层加功能,不用重新做一杯新奶茶。

适用场景

  • 动态给对象添加/撤销功能,不想用继承搞出一堆子类
  • Java IO流(FileInputStream + BufferedInputStream)
  • Spring的TransactionProxyFactoryBean

3. 代理模式

给目标对象创建一个代理对象,通过代理对象控制对目标对象的访问,还能在不修改目标对象的前提下加额外功能。

类比

就像租房找中介,你不用直接对接房东,中介帮你搞定看房、签合同,还能帮你做背景调查。

适用场景

  • 远程代理:RPC调用
  • 权限控制、日志记录、事务管理
  • Spring AOP的动态代理

4. 外观模式

给复杂的子系统提供一个统一的高层入口,隐藏子系统的复杂细节,调用方只需要和这个入口交互就行。

类比

就像酒店前台,你不用管酒店的保洁、维修、餐饮、安保各个部门,有任何事找前台就行,前台帮你对接所有部门。

适用场景

  • 复杂系统简化调用,封装底层细节
  • Spring Boot的starter就是外观模式的思想

5. 桥接模式

把抽象和实现分离,让它们可以独立变化,解决多维度变化导致的类爆炸问题。

类比

就像手机和手机壳,手机有苹果、华为、小米,手机壳有透明、硅胶、卡通,两个维度独立变化,不用为每个手机做一套专属手机壳。

适用于类有多个独立变化的维度,用继承会导致子类数量爆炸的场景。

6. 组合模式

把对象组合成树形结构,用来表示整体-部分的层级关系,让调用方对单个对象和组合对象的使用是一致的。

类比

就像公司组织架构,总公司下面有分公司,分公司下面有部门,部门下面有员工,不管是总公司、分公司、部门还是员工,都可以统一用组织节点来表示。

适用场景

  • 树形结构的数据,比如菜单、部门组织、文件目录
  • 想要统一处理单个对象和组合对象,不用区分是整体还是部分

7. 享元模式

共享细粒度的对象,减少对象创建的数量,节省内存,核心是复用对象。

类比

就像共享单车,不是每个人都买一辆新自行车,而是大家共享一批单车,用完还回去,别人继续用。

适用场景

  • Java的字符串常量池、Integer缓存池
  • 连接池、线程池(享元模式 + 单例模式的结合体)

行为型模式

解决类和对象之间的通信、职责分配问题,让对象之间协作更灵活、职责更清晰。

1. 策略模式

定义一系列算法,把每个算法封装起来,让它们可以互相替换,算法的变化不会影响使用算法的用户。

类比

就像出行,你可以选骑自行车、坐地铁、开车、坐飞机,这些都是不同的出行策略,你可以根据目的地、预算随便换,不用改变你要出门的目标。

适用场景

  • 一个业务有多种实现方式,需要动态切换
  • 想要避免大量的if-else/switch分支
  • Spring的Resource接口、BeanNameGenerator

代码示例

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
// 1. 策略接口:出行方式
public interface TravelStrategy {
    void travel();
}

// 2. 具体策略:坐地铁
public class SubwayStrategy implements TravelStrategy {
    @Override
    public void travel() {
        System.out.println("坐地铁出行,便宜又快");
    }
}

// 3. 具体策略:开车
public class CarStrategy implements TravelStrategy {
    @Override
    public void travel() {
        System.out.println("开车出行,自由方便");
    }
}

// 4. 用户
public class Traveler {
    private TravelStrategy strategy;

    // 动态设置策略
    public void setStrategy(TravelStrategy strategy) {
        this.strategy = strategy;
    }

    // 执行出行
    public void goTravel() {
        strategy.travel();
    }
}

// 测试
public class Test {
    public static void main(String[] args) {
        Traveler traveler = new Traveler();
        // 今天坐地铁
        traveler.setStrategy(new SubwayStrategy());
        traveler.goTravel();
        // 明天开车
        traveler.setStrategy(new CarStrategy());
        traveler.goTravel();
    }
}

2. 观察者模式

定义了对象之间一对多的依赖关系,当一个对象的状态发生变化时,所有依赖它的对象都会收到通知并自动更新。

类比

就像你关注了一个CSDN博主,博主更新博客了,所有关注他的粉丝都会收到推送通知。

适用场景

  • 事件驱动、消息通知、发布订阅
  • Spring的ApplicationEvent事件机制
  • Java的EventListener

3. 模板方法模式

定义一个操作的算法骨架,把其中一些步骤延迟到子类实现,子类可以不改变算法骨架,就可以重定义特定步骤。

类比

就像煮奶茶,固定骨架是煮茶➡️加奶➡️加料➡️装杯,不同的奶茶只是加料、煮茶时间不一样,骨架永远不变。

小贴士

煮奶茶是模板方法模式,关注的是怎么做奶茶,而开奶茶店造奶茶是工厂方法模式,关注的是造什么奶茶。

适用场景

  • 多个子类有公共的流程,只有个别步骤不同
  • Spring的JdbcTemplate、RedisTemplate
  • Spring MVC的HttpServlet

4. 责任链模式

把请求的发送者和接收者解耦,让多个对象都有机会处理这个请求,把这些对象连成一条链,请求沿着链传递,直到有对象处理它为止。

类比

就像公司请假审批,1天以内组长批,3天以内经理批,7天以内总监批,超过7天老板批,你的请假申请沿着这个审批链传递,直到有人处理。

适用场景

  • 一个请求需要多个对象依次处理
  • Spring MVC的HandlerInterceptor拦截器链
  • Servlet的Filter过滤器链

5. 状态模式

当一个对象的内部状态发生变化时,改变它的行为,看起来就像这个对象换了一个类。

类比

就像电梯,开门状态下只能关门,不能上下行;运行状态下只能停梯,不能开门;不同的状态,电梯的行为完全不一样。

适用场景

  • 对象的行为由状态决定,运行时状态变化会改变行为
  • 代码里有大量和状态相关的if-else/switch分支

小贴士

状态模式和策略模式容易混淆,不过两者的区别是:

  1. 状态模式是状态决定行为,对象的行为由当前状态决定;而策略模式是算法可替换,封装一系列平级算法,想换哪个换哪个。
  2. 状态模式有明确的状态流转图,状态之间不能随便跳,必须按规则流转;策略模式没有流转关系,策略之间是平级的,想换就换。
  3. 状态模式关注的是对象状态的变化以及状态变化带来的行为变化;策略模式关注的是算法的选择和替换,不关心算法之间的关系。

扯了那么多官话,好像没多大用,举个例子套一下大概就是:

比如电梯是状态模式,开门、上下行、关门有明确的流转规则;

而出行是策略模式,骑自行车、坐地铁、开车是平级的,想换就换。

6. 命令模式

把一个请求封装成一个命令对象,让你可以用不同的请求参数化对象,支持请求排队、撤销、重做。

类比

就像餐厅点餐,你点的菜就是一个命令,服务员把命令传给后厨,后厨执行,你不用管后厨是谁、怎么做的,还能随时加菜、退菜。

适用场景

  • Spring 的 JmsTemplate(JMS 消息队列)和 RabbitTemplate(RabbitMQ 消息队列)

7. 迭代器模式

提供一种统一的方式,遍历一个集合对象里的所有元素,不用暴露集合的内部结构。

类比

就像景区观光车,不管景区里的景点是怎么分布的,你坐观光车就能一个一个逛完所有景点,不用管景区内部的路线设计。

适用场景

  • Java的Iterator接口、增强for循环
  • Spring的CompositeIterator

8. 中介者模式

用一个中介对象封装一系列对象的交互,让对象之间不用直接互相引用,解耦对象之间的复杂依赖,只和中介者交互。

类比

就像机场塔台,所有飞机不用互相沟通,只需要和塔台沟通,塔台统一调度,避免撞机。

适用场景

  • 想要解耦多个对象的交互,减少耦合
  • Spring MVC的DispatcherServlet,就是前端控制器中介者

9. 备忘录模式

在不破坏封装的前提下,捕获一个对象的内部状态,并保存下来,之后可以恢复到这个状态。

类比

就像游戏存档,你打BOSS之前存个档,打输了可以读档回到存档时的状态,不用重新从头玩。

适用场景

  • 撤销、回滚操作
  • 数据库的事务回滚
  • IDE的撤销操作

10. 访问者模式

在不修改数据结构的前提下,定义对数据结构中元素的新操作,把数据结构和操作分离。

类比

比如电商订单系统,数据结构是固定的订单和订单明细,操作是经常变化的计算总价、打印发票、生成物流单、发送短信通知。不用修改订单 / 订单明细的代码,只要新增一个访问者(比如生成电子发票),就能给订单增加新操作。

适用于想要给数据结构里的元素增加新操作,又不想修改数据结构的场景。

11. 解释器模式

给定一个语言,定义它的语法规则,然后定义一个解释器,用这个解释器来解释语言中的句子。

类比

就像翻译官,你说中文,翻译官根据中英文的语法规则,把你的话翻译成英文。

适用场景

  • SQL解析、正则表达式解析
  • Spring的SpEL表达式
Code Road Record