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

前言

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

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

  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请求对象等。
  • 需要保证对象创建完整性:如创建"有效订单"必须包含订单号、商品、金额等必选参数。
  • 复杂表单或请求对象的构建:如用户注册请求、订单提交请求、报表筛选条件等。

与其他模式对比

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

总结

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

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

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

相关推荐
小白学大数据2 小时前
Python爬虫实现无限滚动页面的自动点击与内容抓取
开发语言·爬虫·python·pandas
兩尛2 小时前
c++遍历容器(vector、list、set、map
开发语言·c++
belldeep2 小时前
Java:Tomcat 9 和 mermaid.min.js 10.9 上传.csv文件实现 Markdown 中 Mermaid 图表的渲染
java·tomcat·mermaid·去除flexmark
£漫步 云端彡2 小时前
Golang学习历程【第十三篇 并发入门:goroutine + channel 基础】
开发语言·学习·golang
2301_790300962 小时前
C++与Docker集成开发
开发语言·c++·算法
AutumnorLiuu2 小时前
C++并发编程学习(二)—— 线程所有权和管控
java·c++·学习
Demon_Hao2 小时前
JAVA缓存的使用RedisCache、LocalCache、复合缓存
java·开发语言·缓存
踏雪羽翼2 小时前
android 解决混淆导致AGPBI: {“kind“:“error“,“text“:“Type a.a is defined multiple times
android·java·开发语言·混淆·混淆打包出现a.a
lang201509282 小时前
Tomcat Maven插件:部署与卸载的架构设计
java·tomcat·maven