一文漫谈设计模式之创建型模式(一)

前言

设计模式的重要性可以概括为:提供经过验证的通用解决方案,提升代码质量、可维护性、可扩展性和团队协作效率。

具体来说,设计模式通过以下方式发挥作用:

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

创建型模式通过将对象的创建与使用分离,解决了硬编码创建导致的耦合问题,使系统在对象创建方式变化时更具灵活性和可维护性。

该系列其他文章:

面向对象基础

封装、继承和多态是面向对象编程(OOP)的三大核心原则,它们共同构成了构建灵活、可维护和可复用代码的基石。下面这个表格能帮你快速把握它们的核心要义。

特性 核心思想 主要目的 关键实现方式
封装 将数据和对数据的操作捆绑在一起,隐藏内部实现细节 增强安全性,降低耦合度,提高可维护性 使用 privateprotected 等访问修饰符
继承 基于已有类创建新类,继承其属性和方法 实现代码复用,建立类之间的层次关系 通过 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请求对象等。
  • 需要保证对象创建完整性:如创建"有效订单"必须包含订单号、商品、金额等必选参数。
  • 复杂表单或请求对象的构建:如用户注册请求、订单提交请求、报表筛选条件等。

与其他模式对比

对比维度 建造者模式 工厂模式 抽象工厂模式
构造目标 多部分组成的复杂对象 单个标准产品 多个相关对象族
构造过程 分步骤渐进式构建 一步构造完成 一次性创建完整产品族
关注重点 对象的组装过程 对象的创建行为 保证产品族的兼容性

总结

建造者模式是处理复杂对象创建的强大工具,特别适合那些构建过程稳定但产品表示多样化的场景。

通过将构建过程分解为多个步骤,它实现了构建逻辑的复用和扩展,同时保证了对象创建的完整性和可控性。

在实际应用中,需要根据业务复杂度合理选择是否使用该模式,避免过度设计带来的维护成本。

相关推荐
Volunteer Technology9 小时前
架构面试题(一)
开发语言·架构·php
清水白石0089 小时前
Python 对象序列化深度解析:pickle、JSON 与自定义协议的取舍之道
开发语言·python·json
2401_876907529 小时前
Python机器学习实践指南
开发语言·python·机器学习
努力中的编程者10 小时前
栈和队列(C语言底层实现环形队列)
c语言·开发语言
回到原点的码农10 小时前
Spring Data JDBC 详解
java·数据库·spring
gf132111110 小时前
python_查询并删除飞书多维表格中的记录
java·python·飞书
zb2006412010 小时前
Spring Boot 实战:轻松实现文件上传与下载功能
java·数据库·spring boot
一勺菠萝丶10 小时前
Flowable + Spring 集成踩坑:流程结束监听器查询历史任务为空 & 获取不到审批意见
java·数据库·spring
jwn99910 小时前
Spring Boot 整合 Keycloak
java·spring boot·后端
宁波阿成10 小时前
OpenClaw 在 Ubuntu 22.04.5 LTS 上的安装与问题处理记录
java·linux·ubuntu·openclaw·龙虾