前言
设计模式的重要性可以概括为:提供经过验证的通用解决方案,提升代码质量、可维护性、可扩展性和团队协作效率。
具体来说,设计模式通过以下方式发挥作用:
- 提高代码质量和可维护性:设计模式将最佳实践封装为可复用的模板,使代码结构更清晰、职责更单一,便于理解和修改,减少错误和冗余。
- 增强可扩展性和灵活性:通过解耦对象创建、结构和行为,设计模式使系统更容易扩展新功能,而无需大规模修改现有代码,符合"开闭原则"。
- 促进代码复用和开发效率:设计模式提供现成的解决方案,避免重复造轮子,缩短开发周期,降低开发成本。
- 改善团队协作:设计模式为开发者提供共同的"设计语言",使团队成员能更高效地沟通和理解彼此的设计意图。
- 提升系统可靠性和可测试性:模块化的设计使代码更易于测试和验证,减少潜在缺陷,提高软件稳定性。
创建型模式通过将对象的创建与使用分离,解决了硬编码创建导致的耦合问题,使系统在对象创建方式变化时更具灵活性和可维护性。

该系列其他文章:
面向对象基础
封装、继承和多态是面向对象编程(OOP)的三大核心原则,它们共同构成了构建灵活、可维护和可复用代码的基石。下面这个表格能帮你快速把握它们的核心要义。
| 特性 | 核心思想 | 主要目的 | 关键实现方式 |
|---|---|---|---|
| 封装 | 将数据和对数据的操作捆绑在一起,隐藏内部实现细节 | 增强安全性,降低耦合度,提高可维护性 | 使用 private、protected 等访问修饰符 |
| 继承 | 基于已有类创建新类,继承其属性和方法 | 实现代码复用,建立类之间的层次关系 | 通过 extends(Java)或 :(C++)关键字 |
| 多态 | 同一操作作用于不同对象,可以产生不同的执行结果 | 增强程序的灵活性和可扩展性 | 通过方法重写、虚函数(C++)、接口(Java)等 |
在C++或Java等面向对象语言中,这些概念通过特定语法实现:
- 封装:通过private关键字将成员变量私有化,并通过公共的成员函数提供访问接口。
- 继承:使用class DerivedClass : public BaseClass(C++)或class SubClass extends SuperClass(Java)的语法实现继承。
- 多态:在C++中主要通过虚函数(virtual function) 和基类指针或引用来实现;在Java中,所有非final的方法默认都是可被重写的,通过基类引用调用子类重写的方法即可体现多态。
简单来说,你可以这样理解它们的关系:
- 封装 关注的是类的内部结构,目标是"高内聚,低耦合"。
- 继承 关注的是类与类之间的关系,目标是代码复用和层次化。
- 多态则是建立在封装和继承之上的运行时的行为特性,目标是实现接口统一和程序的可扩展性。
创建型模式
创建型模式是软件工程中用于处理对象创建的设计模式,它们通过将对象的创建过程抽象化,使系统与具体对象的类型、创建方式解耦,从而提升代码的灵活性、可复用性和可维护性。
| 模式名称 | 核心思想与简要说明 |
|---|---|
| 单例模式 | 确保一个类只有一个实例,并提供一个全局访问点。常用于资源管理,如配置管理或连接池。 |
| 原型模式 | 通过复制一个现有的原型对象来创建新对象,而非通过new关键字。适用于创建成本较高的对象或需要动态配置对象的场景。 |
| 工厂方法模式 | 定义一个用于创建对象的接口,但让子类决定实例化哪一个类。它将类的实例化推迟到子类,符合"开闭原则"。 |
| 抽象工厂模式 | 提供一个接口,用于创建相关或依赖对象的家族,而不需要指定它们具体的类。它强调的是一系列相关产品的创建。 |
| 建造者模式 | 将一个复杂对象的构建与其表示分离,使得同样的构建过程可以创建不同的表示。特别适用于创建需要多个步骤、配置复杂的对象。 |
单例模式
单例模式是一种创建型设计模式,它确保一个类只有一个实例,并提供一个全局访问点来获取这个实例。这在需要控制资源(如数据库连接池、配置管理器)或保证状态一致性的场景中非常有用。
实现单例模式主要围绕以下几个关键点:
- 私有化构造函数:防止外部通过 new关键字随意创建实例。
- 静态私有成员变量:在类内部保存这个唯一的实例。
- 公共静态方法:作为全局访问点,负责返回类的唯一实例。
单例模式的实现方式
单例模式有多种实现方式,它们在线程安全性、延迟加载(懒加载)和实现复杂度上有所不同。下表对比了几种经典实现:
| 实现方式 | 线程安全 | 延迟加载 | 关键特点/原理 | 推荐指数 |
|---|---|---|---|---|
| 饿汉式 | ✅ 安全 | ❌ 不支持 | 类加载时即初始化实例。实现简单,但可能造成资源浪费。 | ⭐⭐⭐ |
| 懒汉式(基础版) | ❌ 不安全 | ✅ 支持 | 首次调用时创建实例。实现简单,但多线程下不安全。 | ⭐ |
| 懒汉式(同步方法) | ✅ 安全 | ✅ 支持 | 在getInstance方法上加synchronized锁。线程安全但效率低。 |
⭐⭐ |
| 双重校验锁(DCL) | ✅ 安全 | ✅ 支持 | 两次检查实例是否为null,配合volatile关键字避免指令重排序。 |
⭐⭐⭐⭐ |
| 静态内部类 | ✅ 安全 | ✅ 支持 | 利用类加载机制保证线程安全,由JVM在首次使用时加载内部类并初始化实例。 | ⭐⭐⭐⭐⭐ |
| 枚举 | ✅ 安全 | ❌ 不支持(本质饿汉式) | 写法简洁,由JVM保证实例唯一性,能防止反射和反序列化破坏单例。 | ⭐⭐⭐⭐⭐ |
案例
java
public class Singleton {
// 1. 私有化构造函数
private Singleton() {
// 防止外部实例化
}
// 2. 静态内部类持有唯一实例
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
// 3. 公共静态方法,作为全局访问点
public static Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
// 示例方法
public void showMessage() {
System.out.println("Hello from the singleton instance!");
}
}
java
public class Main {
public static void main(String[] args) {
// 获取单例实例
Singleton instance = Singleton.getInstance();
// 调用方法
instance.showMessage(); // 输出: Hello from the singleton instance!
// 再次获取实例,验证是否为同一个对象
Singleton anotherInstance = Singleton.getInstance();
System.out.println(instance == anotherInstance); // 输出: true
}
}
这里我们再举一个双重校验锁的例子,其他方式后面我有空会再单独写一篇博客介绍。
双重校验锁(Double-Checked Locking)是一种用于实现单例模式的技术,旨在减少多线程环境下的同步开销,同时确保线程安全。
java
public class Singleton {
// 使用volatile关键字修饰实例变量
private static volatile Singleton instance;
// 私有构造函数,防止外部直接实例化
private Singleton() {
System.out.println("Singleton instance created");
}
// 双重校验锁的getInstance方法
public static Singleton getInstance() {
// 第一次检查:避免不必要的同步
if (instance == null) {
// 同步代码块,确保线程安全
synchronized (Singleton.class) {
// 第二次检查:防止多次实例化
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
关键点解析
(1) volatile关键字的作用
禁止指令重排序:instance = new Singleton()在JVM中分为三步:1分配内存空间;2初始化对象;3将实例指向内存地址。
若未使用volatile,JVM可能按1->3->2的顺序执行(指令重排序)。此时若线程A执行到步骤3(未初始化),线程B在第一次检查时可能直接返回未初始化的实例,导致错误。
保证可见性:确保多线程环境下,一个线程对instance的修改能立即被其他线程看到。
(2) 双重检查的必要性:
第一次检查:避免每次调用getInstance都进入同步块,提升性能。
第二次检查:防止多个线程同时通过第一次检查后,重复创建实例。例如,线程A和B均通过第一次检查,A先获取锁并创建实例后,B再次获取锁时需第二次检查以避免重复创建。
单例模式的应用场景
单例模式适用于那些需要全局唯一实例的场景,例如:
- 资源管理器:如线程池、缓存、日志对象、对话框、打印机驱动程序等。
- 配置信息类:管理全局配置设置,保证所有部分读取的配置一致。
- 计数器:需要全局统一计数的场合。
- 任务管理器(如Windows任务管理器),确保系统中只有一个实例在运行。
总结
单例模式通过控制实例数量,有效地节约了系统资源,并提供了对唯一实例的受控访问。
在选择具体实现方式时,如果追求简单且不介意启动时就加载,可以选择饿汉式或枚举;
如果希望延迟加载并且保证线程安全,静态内部类和双重校验锁都是不错的选择。
原型模式
原型模式是一种创建型设计模式,它通过复制现有对象来创建新对象,而不是通过new关键字实例化。这种方式特别适用于创建成本较高的对象,或需要动态配置对象的场景。
核心思想:用一个已经存在的对象作为"原型",通过复制(克隆)这个原型来生成新的对象实例。
主要角色:
(1)抽象原型类:通常是一个接口或抽象类,声明了克隆方法(在Java中常用Cloneable接口)。
(2)具体原型类:实现克隆方法的具体类,是可被复制的对象。
(3)客户端:通过调用具体原型类的克隆方法来创建新对象。
关键实现:在Java中,实现原型模式通常需要以下两步:
(1)让具体原型类实现 Cloneable接口(这是一个标记接口,表示该类支持克隆)。
(2)重写 Object类中的 clone()方法,并将其访问修饰符改为 public。
浅克隆与深克隆
这是实现原型模式时需要特别注意的关键概念。
- 浅克隆:只复制对象本身以及其基本数据类型字段。如果对象包含对其他对象的引用,则复制的是引用地址,而不是引用的对象本身。这意味着原对象和克隆对象会共享这些引用对象。直接调用 super.clone()通常实现的就是浅克隆。
- 深克隆:不仅复制对象本身,还会递归地复制对象所引用的所有其他对象。这样,原对象和克隆对象之间完全独立,互不影响。实现深克隆可能需要更复杂的逻辑,例如手动复制引用对象,或使用序列化/反序列化机制。
例子
java
// 1. 具体原型类:奖状(Citation)
class Citation implements Cloneable {
private String name; // 获奖学生姓名
private String info; // 获奖信息
private String college; // 学校
public Citation(String name, String info, String college) {
this.name = name;
this.info = info;
this.college = college;
System.out.println("奖状创建成功!");
}
// 2. 重写 clone 方法(此处为浅克隆)
@Override
public Citation clone() throws CloneNotSupportedException {
System.out.println("奖状拷贝成功!");
return (Citation) super.clone();
}
public void setName(String name) {
this.name = name;
}
public void display() {
System.out.println(name + info + college);
}
}
// 3. 客户端(Client)使用原型模式
public class ProtoTypeCitation {
public static void main(String[] args) throws CloneNotSupportedException {
// 创建原型奖状
Citation citationOriginal = new Citation("张三", "同学:在2023学年表现优秀,被评为三好学生。", "XX大学");
// 通过克隆创建新奖状
Citation citationClone = citationOriginal.clone();
// 修改克隆奖状上的姓名
citationClone.setName("李四");
// 显示奖状
citationOriginal.display(); // 输出:张三同学:在2023学年表现优秀,被评为三好学生。XX大学
citationClone.display(); // 输出:李四同学:在2023学年表现优秀,被评为三好学生。XX大学
// 验证是否为两个不同的对象
System.out.println("两个奖状是同一个对象吗?" + (citationOriginal == citationClone)); // 输出:false
}
}
原型模式的应用场景
原型模式在以下情况下特别有用:
- 创建对象成本较高时:例如,对象需要经过复杂的计算、从数据库或网络加载大量数据才能初始化。
- 需要避免构造函数的约束时:当对象的创建过程非常繁琐,或者不希望产品类和创建代码紧密耦合时。
- 系统需要动态地生成或配置对象时:运行时根据需要克隆原型并修改部分属性,比直接实例化更灵活。
- 需要保存对象状态以备恢复或撤销操作时。
总结
原型模式通过克隆的方式创建新对象,是new关键字的一种替代方案。
它的主要优点是可以隐藏对象创建的细节,提高性能,并动态配置对象。
在Java中实现时,关键在于理解并正确实现Cloneable接口和clone()方法,并特别注意浅克隆与深克隆的区别,根据实际需求选择适当的克隆方式。
工厂模式
工厂方法模式是一种创建型设计模式,它定义一个用于创建对象的接口,但让子类决定实例化哪一个类,从而将对象的创建过程延迟到子类中完成。这种模式的核心思想是依赖抽象而非具体实现,有效降低了客户端与具体产品类之间的耦合度。
核心角色与结构
工厂方法模式通常包含以下四个核心角色:
| 角色 | 说明 |
|---|---|
| 抽象产品 | 定义了产品的通用接口,是所有具体产品类的父类或共同实现的接口。 |
| 具体产品 | 实现了抽象产品接口,是工厂方法模式所创建的具体实例对象。 |
| 抽象工厂 | 声明了工厂方法,该方法返回一个抽象产品类型的对象,是工厂方法模式的核心。 |
| 具体工厂 | 继承或实现了抽象工厂,负责实例化特定的具体产品对象。 |
代码示例
java
// 1. 抽象产品:日志记录器接口
public interface Logger {
void log(String message);
}
// 2. 具体产品:文件日志记录器
public class FileLogger implements Logger {
@Override
public void log(String message) {
System.out.println("File Logger: " + message);
}
}
// 3. 具体产品:数据库日志记录器
public class DatabaseLogger implements Logger {
@Override
public void log(String message) {
System.out.println("Database Logger: " + message);
}
}
// 4. 抽象工厂:日志工厂接口
public interface LoggerFactory {
Logger createLogger();
}
// 5. 具体工厂:文件日志工厂
public class FileLoggerFactory implements LoggerFactory {
@Override
public Logger createLogger() {
return new FileLogger();
}
}
// 6. 具体工厂:数据库日志工厂
public class DatabaseLoggerFactory implements LoggerFactory {
@Override
public Logger createLogger() {
return new DatabaseLogger();
}
}
// 7. 客户端使用示例
public class Client {
public static void main(String[] args) {
// 使用文件日志
LoggerFactory fileFactory = new FileLoggerFactory();
Logger fileLogger = fileFactory.createLogger();
fileLogger.log("This is a file log.");
// 使用数据库日志
LoggerFactory dbFactory = new DatabaseLoggerFactory();
Logger dbLogger = dbFactory.createLogger();
dbLogger.log("This is a database log.");
}
}
优势与适用场景
工厂方法模式的主要优点在于:
- 良好的解耦性:客户端只需要关心抽象工厂和抽象产品,无需知晓具体实现类。
- 强大的扩展性:添加新的产品类型时,只需增加新的具体工厂和具体产品类,无需修改现有代码,符合开闭原则。
- 代码可维护性:将对象的创建逻辑集中管理,使代码结构更清晰。
其典型应用场景包括:
- 创建过程比较复杂或需要统一管理的对象。
- 系统需要具备良好的扩展性,预计未来会频繁增加新的产品类型。
- 需要隔离具体类的创建细节,避免客户端代码依赖具体实现。
与其他工厂模式的简单对比
| 模式 | 核心特点 | 适用场景 |
|---|---|---|
| 简单工厂模式 | 一个工厂类通过条件判断来创建不同的产品。不符合开闭原则,新增产品需修改工厂类。 | 产品种类固定且较少,无需频繁扩展的场景。 |
| 工厂方法模式 | 每个具体产品对应一个具体工厂。符合开闭原则,扩展性强。 | 产品结构复杂或需要高度灵活扩展的场景。 |
| 抽象工厂模式 | 一个工厂能创建多个产品族(一系列相关或依赖的产品)。 | 需要创建一系列相关或依赖产品的场景。 |
总结
工厂模式是一组创建型设计模式的统称,核心思想是将对象的创建过程封装起来,让客户端不直接依赖具体实现类,而是通过工厂来获取对象实例。工厂模式主要包括三种类型:简单工厂模式、工厂方法模式和抽象工厂模式。
简单工厂模式通过一个工厂类根据传入参数创建不同产品,虽然实现简单但违反了开闭原则,新增产品需要修改工厂类代码,适用于产品种类固定且变化较少的场景。
工厂方法模式为每个产品定义一个对应的工厂类,客户端通过抽象工厂接口获取产品,完全符合开闭原则,扩展性强,是工厂模式中最常用的一种。
抽象工厂模式用于创建产品族(一系列相关或依赖的产品),一个工厂可以创建多个产品,适合需要创建一组相关对象的复杂场景。
工厂模式的主要优势在于解耦、提高扩展性和可维护性,让系统更容易应对变化。在实际开发中,工厂方法模式应用最为广泛,而简单工厂模式适合简单场景,抽象工厂模式则用于产品族创建需求。
抽象工厂模式
抽象工厂模式是一种创建型设计模式,它提供一个接口,用于创建一系列相关或相互依赖的对象,而无需指定它们的具体类。这种模式特别适用于需要确保一系列相关产品一起使用的场景。
抽象工厂模式通常包含以下核心角色:
| 角色 | 说明 |
|---|---|
| 抽象工厂 | 声明一组用于创建不同抽象产品的方法接口。 |
| 具体工厂 | 实现抽象工厂接口,负责创建属于同一产品族的具体产品对象。 |
| 抽象产品 | 定义一类产品的接口,如 Button、Checkbox。 |
| 具体产品 | 实现抽象产品接口,是特定产品族的具体产品,如 WindowsButton、MacOSButton。 |
为何单独拎出来
抽象工厂模式被单独列为23种设计模式之一,而简单工厂模式未被纳入,主要是因为抽象工厂模式解决了一类独特的、更复杂的设计问题,其核心思想、结构和适用场景与工厂方法模式有本质区别。
| 特性 | 工厂方法模式 | 抽象工厂模式 |
|---|---|---|
| 核心关系 | 一个工厂创建一个产品(一对一) | 一个工厂创建一族产品(一对多) |
| 核心思想 | 将对象的实例化延迟到子类 | 创建相关或依赖对象的家族,而不需要指定它们具体的类 |
| 产品维度 | 单一产品等级结构 | 多个产品等级结构(产品族) |
| 扩展侧重点 | 易于增加新的产品等级结构(新产品类型) | 易于增加新的产品族(新的品牌或系列) |
| "开闭原则" | 对增加新产品开放,对修改工厂关闭 | 对增加新产品族开放,对增加新产品类型关闭 |
简单来说,工厂方法模式关注的是如何创建一个特定类型的对象,而抽象工厂模式关注的是如何创建一整组有约束关系的对象家族。正是这种在抽象层次和解决问题复杂度上的显著差异,使得抽象工厂模式成为了一个独立且重要的设计模式。
例子
模拟创建不同操作系统风格(如Windows和macOS)UI组件的抽象工厂模式简单实现。
1.定义抽象产品
首先定义UI组件的抽象接口:按钮和复选框。
java
// 抽象产品:按钮
public interface Button {
void click();
}
// 抽象产品:复选框
public interface Checkbox {
void select();
}
2.定义具体产品
为每个产品族创建具体的产品类。
java
// Windows 产品族的具体产品
public class WindowsButton implements Button {
@Override
public void click() {
System.out.println("Windows 风格的按钮被点击了。");
}
}
public class WindowsCheckbox implements Checkbox {
@Override
public void select() {
System.out.println("Windows 风格的复选框被选中了。");
}
}
// macOS 产品族的具体产品
public class MacOSButton implements Button {
@Override
public void click() {
System.out.println("macOS 风格的按钮被点击了。");
}
}
public class MacOSCheckbox implements Checkbox {
@Override
public void select() {
System.out.println("macOS 风格的复选框被选中了。");
}
}
3.定义抽象工厂
抽象工厂声明创建整个产品族的方法。
java
public interface GUIFactory {
Button createButton();
Checkbox createCheckbox();
}
4.定义具体工厂
每个具体工厂负责创建属于特定产品族的所有产品。
java
// 创建 Windows 产品族
public class WindowsFactory implements GUIFactory {
@Override
public Button createButton() {
return new WindowsButton();
}
@Override
public Checkbox createCheckbox() {
return new WindowsCheckbox();
}
}
// 创建 macOS 产品族
public class MacOSFactory implements GUIFactory {
@Override
public Button createButton() {
return new MacOSButton();
}
@Override
public Checkbox createCheckbox() {
return new MacOSCheckbox();
}
}
5.客户端使用
客户端代码通过抽象工厂接口操作,从而与具体产品解耦。
java
public class Application {
private Button button;
private Checkbox checkbox;
// 通过注入不同的具体工厂来配置应用
public Application(GUIFactory factory) {
button = factory.createButton();
checkbox = factory.createCheckbox();
}
public void paintUI() {
button.click();
checkbox.select();
}
public static void main(String[] args) {
// 配置为使用 Windows 风格
GUIFactory windowsFactory = new WindowsFactory();
Application app = new Application(windowsFactory);
app.paintUI(); // 输出: Windows风格的按钮被点击了。 Windows风格的复选框被选中了。
// 配置为使用 macOS 风格
GUIFactory macFactory = new MacOSFactory();
Application app2 = new Application(macFactory);
app2.paintUI(); // 输出: macOS风格的按钮被点击了。 macOS风格的复选框被选中了。
}
}
模式的优势与局限
主要优点:
- 保证产品兼容性:确保客户端使用的所有产品都来自同一个产品族,避免了不匹配的问题。
- 便于切换产品族:更换具体工厂即可让客户端使用一套全新的相关产品,符合开闭原则。
- 解耦客户端与具体类:客户端代码只依赖于抽象接口,与具体产品的实现细节分离。
主要局限:
难以支持新种类产品:若要在产品族中增加一个全新种类的产品(如新增一个TextBox),需要修改抽象工厂及其所有具体工厂实现,扩展性较差。
总结
抽象工厂模式是处理多个相关产品族创建的强大工具。
当你的系统需要在多个相关的产品家族之间进行切换,并且要保证家族内产品的兼容性时,抽象工厂模式是一个非常合适的选择。
它的核心价值在于隔离了具体类的生成,使得客户端代码只需要关注抽象接口
建造者模式
建造者模式是一种创建型设计模式,它将复杂对象的构建过程与其表示分离,使得同样的构建过程可以创建不同的对象表示。这种模式特别适用于创建具有多个组成部分或配置选项的复杂对象
建造者模式的核心在于分步构建复杂对象,通过明确的步骤和接口,使构建过程更清晰、灵活,并且可以复用相同的构建过程来创建不同的产品。
该模式通常涉及以下四个核心角色:
| 角色 | 职责说明 |
|---|---|
| 产品 | 最终要构建的复杂对象,包含多个部件。 |
| 抽象建造者 | 定义创建产品各个部件的抽象接口。 |
| 具体建造者 | 实现抽象建造者接口,完成产品各部件的具体构造和装配。 |
| 指挥者 | 负责控制构建流程,按照特定顺序调用建造者的方法。 |
案例:组装电脑
下面通过一个组装电脑的例子来演示建造者模式的实现。产品类 Computer包含多个属性:
java
// 1. 产品类 - Computer
public class Computer {
private String cpu;
private String ram;
private String storage;
private String gpu;
// 省略getter和setter方法
@Override
public String toString() {
return "Computer [CPU=" + cpu + ", RAM=" + ram + ", Storage=" + storage + ", GPU=" + gpu + "]";
}
}
抽象建造者定义了构建步骤:
java
// 2. 抽象建造者 - ComputerBuilder
public interface ComputerBuilder {
void buildCPU();
void buildRAM();
void buildStorage();
void buildGPU();
Computer getResult();
}
具体建造者实现这些步骤以构建特定配置的产品:
java
// 3. 具体建造者 - GamingComputerBuilder
public class GamingComputerBuilder implements ComputerBuilder {
private Computer computer;
public GamingComputerBuilder() {
this.computer = new Computer();
}
@Override
public void buildCPU() {
computer.setCpu("Intel i9");
}
@Override
public void buildRAM() {
computer.setRam("32GB DDR5");
}
@Override
public void buildStorage() {
computer.setStorage("1TB NVMe SSD");
}
@Override
public void buildGPU() {
computer.setGpu("NVIDIA RTX 4080");
}
@Override
public Computer getResult() {
return computer;
}
}
指挥者类指导构建过程:
java
// 4. 指挥者 - ComputerDirector
public class ComputerDirector {
private ComputerBuilder builder;
public ComputerDirector(ComputerBuilder builder) {
this.builder = builder;
}
public Computer construct() {
builder.buildCPU();
builder.buildRAM();
builder.buildStorage();
builder.buildGPU();
return builder.getResult();
}
}
客户端通过指挥者来构建产品:
java
// 5. 客户端使用
public class Client {
public static void main(String[] args) {
ComputerBuilder builder = new GamingComputerBuilder();
ComputerDirector director = new ComputerDirector(builder);
Computer gamingPC = director.construct();
System.out.println(gamingPC);
}
}
简化变体:静态内部类建造者
在实际开发中,建造者模式常以简化的形式出现,例如使用静态内部类实现链式调用,这在创建有许多可选参数的对象时特别有用,能有效避免构造器参数过长的问题。
java
public class Computer {
private final String cpu;
private final String ram;
private final String storage;
private final String gpu;
// 私有构造方法,通过Builder构建
private Computer(Builder builder) {
this.cpu = builder.cpu;
this.ram = builder.ram;
this.storage = builder.storage;
this.gpu = builder.gpu;
}
// 静态内部类Builder
public static class Builder {
private String cpu; // 必需
private String ram; // 必需
private String storage = "512GB SSD"; // 可选,含默认值
private String gpu = "Integrated"; // 可选,含默认值
public Builder(String cpu, String ram) {
this.cpu = cpu;
this.ram = ram;
}
public Builder storage(String storage) {
this.storage = storage;
return this; // 支持链式调用
}
public Builder gpu(String gpu) {
this.gpu = gpu;
return this;
}
public Computer build() {
// 可在build()方法中进行参数校验
return new Computer(this);
}
}
// 省略getter方法
}
// 客户端使用简化版建造者
public class Client {
public static void main(String[] args) {
Computer myPC = new Computer.Builder("Intel i7", "16GB")
.storage("1TB SSD")
.gpu("NVIDIA GTX 3070")
.build();
System.out.println(myPC);
}
}
优势与适用场景
建造者模式的优势包括:
- 良好的封装性:将复杂对象的构建过程隐藏起来,使用者无需关心内部细节。
- 灵活的构建过程:可以通过不同的具体建造者,或改变指挥者的构建顺序,轻松创建不同的产品表示。
- 更好的控制力:将构建过程分解为多个步骤,可以对构建流程进行更精细的控制。
- 符合开闭原则:增加新的具体建造者无需修改现有代码,系统扩展方便。
适用场景主要有:
- 复杂对象的构建:需要多个步骤构建的对象,如嵌套、依赖其他对象的初始化等。
- 构建过程稳定,产品表示多样化:不同的表示方式(如不同配置的车辆、不同风格的房屋)。
- 对象构造步骤依赖顺序:例如必须先初始化再添加组件。
- 参数多且部分可选的对象:如订单对象、用户信息对象、HTTP请求对象等。
- 需要保证对象创建完整性:如创建"有效订单"必须包含订单号、商品、金额等必选参数。
- 复杂表单或请求对象的构建:如用户注册请求、订单提交请求、报表筛选条件等。
与其他模式对比
| 对比维度 | 建造者模式 | 工厂模式 | 抽象工厂模式 |
|---|---|---|---|
| 构造目标 | 多部分组成的复杂对象 | 单个标准产品 | 多个相关对象族 |
| 构造过程 | 分步骤渐进式构建 | 一步构造完成 | 一次性创建完整产品族 |
| 关注重点 | 对象的组装过程 | 对象的创建行为 | 保证产品族的兼容性 |
总结
建造者模式是处理复杂对象创建的强大工具,特别适合那些构建过程稳定但产品表示多样化的场景。
通过将构建过程分解为多个步骤,它实现了构建逻辑的复用和扩展,同时保证了对象创建的完整性和可控性。
在实际应用中,需要根据业务复杂度合理选择是否使用该模式,避免过度设计带来的维护成本。