软件设计模式学习指南

目录

1设计模式概述

1.1 什么是设计模式

设计模式(Design Pattern)是软件工程中针对特定问题的可复用解决方案。它描述了在特定环境下,反复出现的设计问题的解决方案,是经过实践验证的最佳实践总结。

**核心定义:**设计模式 = 特定环境(Context)+ 反复出现的问题(Problem)+ 经过验证的解决方案(Solution)

1.2 设计模式的分类

GoF(Gang of Four,四人组)在1995年出版的《设计模式:可复用面向对象软件的基础》中,将23种经典设计模式按目的分为三大类:

类型 目的 包含模式数量 核心关注点
创建型模式 对象的创建机制 5种 如何实例化对象,隐藏创建细节
结构型模式 类的组合与结构 7种 如何组合类/对象形成更大结构
行为型模式 对象间的交互与职责分配 11种 算法和对象间职责的分配

1.3 设计模式六大原则

单一职责原则 (SRP)

一个类只负责一项职责。降低类的复杂度,提高可读性和可维护性。

开闭原则 (OCP)

对扩展开放,对修改关闭。通过抽象和继承实现功能扩展。

里氏替换原则 (LSP)

子类可以替换父类而不影响程序正确性。确保继承关系的正确性。

依赖倒置原则 (DIP)

高层模块不应依赖低层模块,二者都应依赖抽象。面向接口编程。

接口隔离原则 (ISP)

客户端不应依赖它不需要的接口。将大接口拆分为小接口。

迪米特法则 (LoD)

最少知识原则。一个对象应对其他对象有最少的了解,降低耦合。

考试中常考设计原则的应用场景判断,尤其是开闭原则依赖倒置原则。记住口诀:"单开里依接迪"(单一职责、开闭、里氏替换、依赖倒置、接口隔离、迪米特)。

2创建型模式(5种)

创建型模式关注对象的创建过程,将对象的创建与使用分离,提高系统的灵活性。

2.1 单例模式 (Singleton)

**意图:**确保一个类只有一个实例,并提供一个全局访问点。

**适用场景:**配置管理器、连接池、线程池、缓存、日志对象等需要全局唯一实例的场景。

实现方式:

  • 饿汉式:类加载时创建实例,线程安全,但可能浪费资源

  • 懒汉式:首次使用时创建,需处理线程安全问题

  • 双重检查锁定 (DCL):懒汉式 + volatile + synchronized,兼顾性能与安全

  • 静态内部类:利用类加载机制实现懒加载和线程安全

  • 枚举:最简洁、线程安全、防止反序列化攻击

    // 双重检查锁定实现
    public class Singleton {
    private static volatile Singleton instance; // volatile禁止指令重排序

    复制代码
      private Singleton() {}  // 私有构造方法
      
      public static Singleton getInstance() {
          if (instance == null) {  // 第一次检查,避免不必要的同步
              synchronized (Singleton.class) {
                  if (instance == null) {  // 第二次检查,确保只创建一次
                      instance = new Singleton();
                  }
              }
          }
          return instance;
      }

    }

单例模式是考试高频考点,重点掌握线程安全问题volatile关键字的作用(防止指令重排序导致半初始化对象)。

2.2 工厂方法模式 (Factory Method)

**意图:**定义一个创建对象的接口,让子类决定实例化哪个类。

**适用场景:**当一个类不知道它所需要的对象的类时;当一个类希望由子类来指定所创建的对象时。

结构:

  • Product:抽象产品接口
  • ConcreteProduct:具体产品实现
  • Creator:抽象工厂,声明工厂方法
  • ConcreteCreator:具体工厂,实现工厂方法创建具体产品

2.3 抽象工厂模式 (Abstract Factory)

**意图:**提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。

与工厂方法的区别:

对比维度 工厂方法模式 抽象工厂模式
产品维度 单一产品 产品族(多个相关产品)
工厂数量 一个工厂方法 多个工厂方法(产品族)
扩展性 新增产品类型容易 新增产品族容易,新增产品类型困难
典型应用 日志记录器、数据库访问 UI组件库(跨平台按钮、文本框等)

2.4 建造者模式 (Builder)

**意图:**将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。

**适用场景:**复杂对象的创建(如SQL语句、HTTP请求、配置对象),对象有多个可选参数。

核心角色:

  • Builder:抽象建造者,定义构建步骤

  • ConcreteBuilder:具体建造者,实现构建步骤

  • Director:指挥者,控制构建流程(可选)

  • Product:复杂产品对象

    // 经典建造者模式
    public class Computer {
    private String cpu;
    private String ram;
    private String disk;
    // ... 私有构造,通过Builder创建

    复制代码
      public static class Builder {
          private Computer computer = new Computer();
          
          public Builder cpu(String cpu) {
              computer.cpu = cpu;
              return this;  // 链式调用
          }
          public Builder ram(String ram) {
              computer.ram = ram;
              return this;
          }
          public Computer build() {
              return computer;
          }
      }

    }

    // 使用
    Computer pc = new Computer.Builder()
    .cpu("Intel i9")
    .ram("32GB")
    .build();

2.5 原型模式 (Prototype)

**意图:**通过复制现有对象来创建新对象,而不是新建实例。

**适用场景:**对象创建成本高、需要保存对象状态、避免子类化。

浅拷贝 vs 深拷贝:

  • 浅拷贝:复制基本类型和引用地址,引用类型共享
  • 深拷贝:完全复制所有层级,引用类型也独立复制

Java中通过实现 Cloneable 接口和重写 clone() 方法实现。

3结构型模式(7种)

结构型模式关注如何组合类和对象以形成更大的结构,同时保持结构的灵活和高效。

3.1 适配器模式 (Adapter)

**意图:**将一个类的接口转换成客户希望的另外一个接口,使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。

两种实现方式:

  • 类适配器:通过多重继承实现(Java不支持,C++支持)
  • 对象适配器:通过组合实现,更灵活,是推荐方式

**典型应用:**电源适配器、JDBC驱动、第三方API封装、旧系统接口兼容。

3.2 桥接模式 (Bridge)

**意图:**将抽象部分与它的实现部分分离,使它们都可以独立地变化。

**核心思想:**用组合代替继承,解决多维度变化导致的类爆炸问题。

与适配器的区别: 桥接模式是预先设计 的,用于分离抽象和实现;适配器是事后补救的,用于接口转换。

**典型应用:**跨平台UI框架(Window-Platform)、图形绘制(Shape-Renderer)。

桥接模式常考与适配器模式的区别。记住:桥接是设计,适配是补救。桥接模式解决的是"多维度变化"问题。

3.3 组合模式 (Composite)

**意图:**将对象组合成树形结构以表示"部分-整体"的层次结构,使得用户对单个对象和组合对象的使用具有一致性。

核心角色:

  • Component:组件接口,定义叶子和容器的共同操作
  • Leaf:叶子节点,无子节点
  • Composite:容器节点,包含子组件

**典型应用:**文件系统(文件+目录)、UI组件树、组织架构、菜单系统。

3.4 装饰器模式 (Decorator)

**意图:**动态地给一个对象添加一些额外的职责,比生成子类更灵活。

核心特点:

  • 不改变原有对象的结构
  • 功能可动态添加或移除
  • 可嵌套多个装饰器

典型应用: Java I/O流(BufferedInputStream 包装 FileInputStream)、权限控制、日志记录、缓存。

复制代码
// Java I/O 装饰器模式典型应用
InputStream fis = new FileInputStream("file.txt");           // 基础流
InputStream bis = new BufferedInputStream(fis);              // 添加缓冲
InputStream cis = new CheckedInputStream(bis, new CRC32());  // 添加校验
// 层层包装,动态添加功能

装饰器模式 vs 代理模式:装饰器增强功能 ,代理控制访问。装饰器通常有多个嵌套,代理通常单一。

3.5 外观模式 (Facade)

**意图:**为子系统中的一组接口提供一个一致的界面,定义一个高层接口,使子系统更容易使用。

**核心作用:**简化复杂系统的使用,降低客户端与子系统的耦合。

**典型应用:**API网关、系统启动器、复杂库的简单接口封装。

3.6 享元模式 (Flyweight)

**意图:**运用共享技术有效地支持大量细粒度的对象。

核心概念:

  • 内部状态 (Intrinsic):不随环境变化,可共享
  • 外部状态 (Extrinsic):随环境变化,不可共享

**典型应用:**字符串常量池、线程池、连接池、棋类游戏(围棋棋子共享)。

3.7 代理模式 (Proxy)

**意图:**为其他对象提供一种代理以控制对这个对象的访问。

代理类型:

代理类型 目的 典型场景
远程代理 访问远程对象 RMI、Web Service
虚拟代理 延迟加载 大图片加载、分页查询
保护代理 权限控制 角色权限校验
智能引用 附加操作 引用计数、日志记录
动态代理 运行时创建 Spring AOP、MyBatis

代理模式是考试重点,尤其是动态代理(JDK动态代理基于接口,CGLIB基于继承)。Spring AOP默认使用JDK动态代理,目标类没有实现接口时使用CGLIB。

4行为型模式(11种)

行为型模式关注对象之间的交互和职责分配,定义对象间的通信方式和算法封装。

4.1 责任链模式 (Chain of Responsibility)

**意图:**使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。

**核心结构:**处理器链,每个处理器决定是否处理请求或传递给下一个。

**典型应用:**Servlet过滤器链、审批流程、异常处理链、日志级别处理。

4.2 命令模式 (Command)

**意图:**将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化,对请求排队或记录请求日志,以及支持可撤销的操作。

核心角色:

  • Command:抽象命令接口
  • ConcreteCommand:具体命令,绑定接收者
  • Receiver:命令的真正执行者
  • Invoker:调用者,触发命令执行

**典型应用:**撤销/重做功能、任务队列、宏命令、事务系统。

4.3 解释器模式 (Interpreter)

**意图:**给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。

**适用场景:**特定领域语言(DSL)解析、正则表达式、SQL解析、配置文件解析。

注意:适用于文法简单的场景,复杂文法建议使用解析器生成器(如ANTLR)。

4.4 迭代器模式 (Iterator)

**意图:**提供一种方法顺序访问一个聚合对象中的各个元素,而又不需要暴露该对象的内部表示。

典型应用: Java Iterator 接口、for-each 循环、集合遍历。

4.5 中介者模式 (Mediator)

**意图:**用一个中介对象来封装一系列的对象交互,使各对象不需要显式地相互引用,从而使其耦合松散。

与外观模式的区别:

  • 外观模式是单向的,客户端通过外观访问子系统
  • 中介者模式是双向的,同事对象通过中介者相互通信

**典型应用:**聊天室、MVC控制器、协调多个组件的交互。

4.6 备忘录模式 (Memento)

**意图:**在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,以便以后恢复。

核心角色:

  • Originator:原发器,需要保存状态的对象
  • Memento:备忘录,存储原发器状态
  • Caretaker:负责人,管理备忘录的存储

**典型应用:**游戏存档、撤销操作、事务回滚、浏览器历史记录。

4.7 观察者模式 (Observer)

**意图:**定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。

核心角色:

  • Subject:主题,维护观察者列表,通知变化

  • Observer:观察者接口,定义更新方法

  • ConcreteObserver:具体观察者,实现更新逻辑

    // Java 内置观察者模式(已过时,推荐使用 Reactive Streams)
    public class NewsAgency extends Observable {
    public void setNews(String news) {
    setChanged(); // 标记状态已改变
    notifyObservers(news); // 通知所有观察者
    }
    }

    public class NewsChannel implements Observer {
    @Override
    public void update(Observable o, Object arg) {
    System.out.println("收到新闻: " + arg);
    }
    }

**典型应用:**事件监听、消息订阅、MVC模式、Reactive Programming。

观察者模式 vs 发布-订阅模式:观察者模式中观察者和主题直接耦合 ;发布-订阅模式通过事件总线/消息中间件解耦。Spring的事件机制、MQ都是发布-订阅模式。

4.8 状态模式 (State)

**意图:**允许对象在其内部状态改变时改变它的行为,对象看起来似乎修改了它的类。

与策略模式的区别: 状态模式中状态转换由上下文或状态自身 控制;策略模式由客户端选择策略。

**典型应用:**订单状态流转、线程状态管理、游戏角色状态、工作流引擎。

4.9 策略模式 (Strategy)

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

核心结构:

  • Strategy:策略接口
  • ConcreteStrategy:具体策略实现
  • Context:上下文,持有策略引用

**典型应用:**排序算法选择、支付方式选择、压缩算法选择、促销策略。

策略模式 vs 状态模式:策略模式是客户端选择 算法;状态模式是状态驱动行为变化。策略模式通常没有状态转换,状态模式有明确的状态流转图。

4.10 模板方法模式 (Template Method)

**意图:**定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。

核心结构:

  • AbstractClass:抽象类,定义模板方法和基本方法
  • ConcreteClass:具体类,实现抽象方法

方法类型:

  • 模板方法:定义算法骨架,final修饰不可重写
  • 抽象方法:子类必须实现
  • 具体方法:通用实现,子类可继承或重写
  • 钩子方法:空实现或默认实现,子类可选择性重写

典型应用: Servlet的 doGet/doPost、JUnit测试框架、Spring的 JdbcTemplate

4.11 访问者模式 (Visitor)

**意图:**表示一个作用于某对象结构中的各元素的操作,它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作。

核心思想:数据结构与操作分离,新增操作只需新增访问者,无需修改元素类。

**适用场景:**编译器AST遍历、报表生成、对象结构稳定但操作频繁变化的场景。

缺点:破坏封装,新增元素类困难,违反开闭原则(对元素扩展不开放)。

5模式对比与选择

5.1 易混淆模式对比

模式组 模式A 模式B 核心区别
创建型 工厂方法 抽象工厂 单一产品 vs 产品族
创建型 建造者 工厂方法 复杂对象分步构建 vs 简单对象直接创建
结构型 桥接 适配器 预先设计解耦 vs 事后接口转换
结构型 装饰器 代理 功能增强 vs 访问控制
结构型 外观 中介者 单向简化接口 vs 双向协调通信
行为型 策略 状态 客户端选择算法 vs 状态驱动行为
行为型 观察者 发布-订阅 直接耦合 vs 通过中间件解耦
行为型 模板方法 策略 继承复用算法骨架 vs 组合替换整个算法

5.2 模式选择决策树

创建对象时:

  • 需要唯一实例 → 单例
  • 对象创建复杂,需要分步构建 → 建造者
  • 需要根据条件创建不同对象 → 工厂方法/抽象工厂
  • 需要复制现有对象 → 原型

处理类/对象结构时:

  • 接口不兼容需要转换 → 适配器
  • 需要动态添加功能 → 装饰器
  • 需要控制对象访问 → 代理
  • 需要简化复杂系统接口 → 外观
  • 树形结构,统一处理叶子和容器 → 组合
  • 多维度变化,避免类爆炸 → 桥接
  • 大量相似对象需要共享 → 享元

处理对象交互时:

  • 一个对象变化需要通知多个对象 → 观察者
  • 需要封装请求,支持撤销 → 命令
  • 算法需要灵活替换 → 策略
  • 行为随状态变化 → 状态
  • 算法骨架固定,部分步骤变化 → 模板方法
  • 多个对象有机会处理请求 → 责任链
  • 需要遍历聚合对象 → 迭代器

6架构设计师考试要点

6.1 高频考点

创建型模式
  • 单例模式的线程安全问题
  • 工厂方法 vs 抽象工厂的区别
  • 建造者模式的应用场景
结构型模式
  • 适配器 vs 桥接 vs 装饰器 vs 代理
  • 外观模式的作用
  • 享元模式的内部/外部状态
行为型模式
  • 观察者 vs 发布-订阅
  • 策略 vs 状态
  • 模板方法的钩子方法
  • 责任链的应用
设计原则
  • 六大设计原则的理解
  • 开闭原则的实际应用
  • 依赖倒置原则的体现

6.2 典型考题分析

题型1:场景识别题

给出一个业务场景,要求识别应该使用哪种设计模式。解题关键是抓住场景中的核心矛盾:是创建问题、结构问题还是行为问题?

题型2:模式对比题

给出两个相似模式,要求区分它们的适用场景。重点记忆本文"易混淆模式对比"表格中的核心区别

题型3:代码分析题

给出一段代码,要求识别使用了什么模式,或指出代码违反了什么设计原则。需要熟悉各模式的典型代码结构

6.3 快速记忆口诀

创建型(5种): 单原建工抽(单例、原型、建造者、工厂方法、抽象工厂)

结构型(7种): 桥装适外代组享(桥接、装饰器、适配器、外观、代理、组合、享元)

**行为型(11种):**观策模状命迭解访中责(观察者、策略、模板方法、状态、命令、迭代器、解释器、访问者、中介者、备忘录、责任链)

6.4 备考建议

  1. **理解意图:**每种模式的"意图"是核心,必须准确理解
  2. **对比记忆:**相似模式对比记忆,抓住关键区别
  3. **代码实践:**手写每种模式的简化实现,加深理解
  4. **真题训练:**多做历年真题,熟悉出题套路
  5. **结合框架:**联系Spring、JDK等实际框架中的应用

参考资料

  1. Erich Gamma 等, 《设计模式:可复用面向对象软件的基础》. 软件设计模式经典著作,GoF四人组编写。
  2. 全国计算机专业技术资格考试办公室, 《系统架构设计师教程》. 软考架构师官方教材。
  3. 全国计算机专业技术资格考试办公室, 《系统架构设计师考试大纲》. 软考架构师考试大纲。
  4. Robert C. Martin, 《敏捷软件开发:原则、模式与实践》. 设计原则权威参考。