设计模式之建造者模式
简介
建造者模式是一种创建型设计模式,它将一个复杂对象的构建与其表示分离,使得同样的构建过程可以创建不同的表示。 简单来说,就是将对象的构建过程封装起来,允许用户分步骤地构建复杂对象,而无需了解其内部构造细节。
与工厂模式不同,建造者模式侧重于逐步构建一个复杂的对象,而不是一次性创建。 建造者模式关注的是对象各部分的组装过程,而工厂模式关注的是创建整个对象。
建造者模式的角色
建造者模式通常包含以下几个角色:
- 抽象建造者 (Builder): 定义一个抽象接口,规范产品对象的各个组成部分的建造方法。 这个接口声明了要创建复杂对象的哪些部分,但不涉及具体对象部件的创建。
- 具体建造者 (Concrete Builder): 实现 Builder 接口,针对不同的商业逻辑,具体化复杂对象的各个部分的创建。 在建造过程完成后,提供产品的实例。
- 指挥者 (Director): 调用具体建造者来创建复杂对象的各个部分,控制对象各部分的组装顺序。 指挥者不涉及具体产品的信息,只负责保证对象各部分完整创建或按某种顺序创建。 指挥者是可选的,如果构建过程足够简单,可以直接由客户端来调用具体建造者。
- 产品 (Product): 要创建的复杂对象。
建造者模式的使用场景
以下是一些适合使用建造者模式的场景:
- 需要生成的对象具有复杂的内部结构: 当一个对象包含多个部件,且这些部件的创建逻辑复杂时,可以使用建造者模式将构建过程分解为多个步骤。
- 需要生成的对象内部属性本身相互依赖: 当对象的某些属性依赖于其他属性时,可以使用建造者模式来确保属性之间的正确初始化。
- 需要控制对象的构建过程: 当需要对对象的构建过程进行精细控制,例如指定构建顺序或添加验证逻辑时,可以使用建造者模式。
- 创建不同表示的对象:当相同的构建过程需要生成不同类型的对象时,可以使用不同的具体建造者。
建造者模式的优点
- 封装性: 隐藏了产品内部的构造细节。
- 灵活性: 可以灵活地指定产品的各个部件。
- 可读性: 链式调用使代码更易读。
- 可扩展性: 可以很容易地添加新的建造者类,以构建不同类型的产品。
- 控制: 可以在
build() 方法中添加验证逻辑,保证产品对象的有效性。
- 易于维护: 将复杂的构建过程分解为多个步骤,使得代码更易于理解和维护。
建造者模式与工厂模式的区别
- 关注点不同: 建造者模式侧重于逐步构建一个复杂的对象,而工厂模式侧重于一次性创建对象。
- 复杂度不同: 建造者模式通常用于构建更复杂的对象,而工厂模式可以用于创建相对简单的对象。
- 灵活性不同: 建造者模式可以更灵活地控制对象的构建过程,而工厂模式的灵活性相对较低。
示例
Java 中的 StringBuilder 就是建造者模式的一个典型应用。它将单个字符或字符串逐步组装成最终的字符串。
以下是一个使用建造者模式构建电脑的示例:
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 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147
| class Computer { private String cpu; private String ram; private String storage; private String graphicsCard; private String display;
private Computer(String cpu, String ram, String storage, String graphicsCard, String display) { this.cpu = cpu; this.ram = ram; this.storage = storage; this.graphicsCard = graphicsCard; this.display = display; }
@Override public String toString() { return "电脑{" + "CPU='" + cpu + '\'' + ", RAM='" + ram + '\'' + ", 储存='" + storage + '\'' + ", 显卡='" + graphicsCard + '\'' + ", 显示器='" + display + '\'' + '}'; }
public interface Builder { Builder cpu(String cpu); Builder ram(String ram); Builder storage(String storage); Builder graphicsCard(String graphicsCard); Builder display(String display); Computer build(); }
public static class ComputerBuilder implements Builder { private String cpu; private String ram; private String storage; private String graphicsCard; private String display;
@Override public ComputerBuilder cpu(String cpu) { this.cpu = cpu; return this; }
@Override public ComputerBuilder ram(String ram) { this.ram = ram; return this; }
@Override public ComputerBuilder storage(String storage) { this.storage = storage; return this; }
@Override public ComputerBuilder graphicsCard(String graphicsCard) { this.graphicsCard = graphicsCard; return this; }
@Override public ComputerBuilder display(String display) { this.display = display; return this; }
@Override public Computer build() { if (cpu == null || ram == null) { throw new IllegalStateException("组装电脑,CPU和RAM是必需的。"); } return new Computer(cpu, ram, storage, graphicsCard, display); } } }
class ComputerDirector { private Computer.Builder builder;
public ComputerDirector(Computer.Builder builder) { this.builder = builder; }
public Computer constructGamingComputer() { return builder.cpu("Intel i9").ram("32GB").storage("1TB SSD").graphicsCard("Nvidia RTX 5070").display("27-inch 4K").build(); }
public Computer constructOfficeComputer() { return builder.cpu("Intel i5").ram("16GB").storage("512GB SSD").display("24-inch 1080p").build(); } }
public class BuilderDemo { public static void main(String[] args) {
Computer computer1 = new Computer.ComputerBuilder() .cpu("AMD Ryzen 5") .ram("16GB") .storage("500GB SSD") .graphicsCard("AMD Radeon RX 7700") .display("24 inch") .build();
System.out.println("电脑 1: " + computer1);
Computer.Builder builder = new Computer.ComputerBuilder(); ComputerDirector director = new ComputerDirector(builder); Computer gamingComputer = director.constructGamingComputer(); System.out.println("游戏电脑: " + gamingComputer);
Computer officeComputer = director.constructOfficeComputer(); System.out.println("办公电脑: " + officeComputer);
try { Computer invalidComputer = new Computer.ComputerBuilder().storage("1TB").build(); System.out.println(invalidComputer); } catch (IllegalStateException e) { System.out.println("出现异常: " + e.getMessage()); } } }
|
关键点说明:
- 产品类 (Product):
Computer 类是最终要创建的复杂对象。 注意它的构造函数是私有的,这样就只能通过 Builder 来创建它,保证了建造过程的控制。
- 建造者接口 (Builder Interface):
Computer.Builder 接口定义了创建 Computer 对象各个部分的方法。 它也可以是一个抽象类,如果有一些通用的构建逻辑。
- 具体建造者 (Concrete Builder):
Computer.ComputerBuilder 实现了 Computer.Builder 接口,负责具体的产品构建过程。 它提供链式调用的方式来设置各个属性,并且 build() 方法返回最终的 Computer 对象。
- 指挥者 (Director) - 可选:
ComputerDirector 类负责管理建造者的构建顺序。 它封装了复杂的构建逻辑,客户端只需要选择构建哪种类型的 Computer,而不需要关心构建细节。 如果构建过程很简单,Director 可以省略。
- 客户端 (Client): 客户端代码创建建造者或指挥者,并调用相应的方法来构建
Computer 对象。 客户端无需关心 Computer 对象的构建细节。
运行 BuilderDemo.java 将会打印出:
1 2 3 4
| 电脑 1: 电脑{CPU='AMD Ryzen 5', RAM='16GB', 储存='500GB SSD', 显卡='AMD Radeon RX 7700', 显示器='24 inch'} 游戏电脑: 电脑{CPU='Intel i9', RAM='32GB', 储存='1TB SSD', 显卡='Nvidia RTX 5070', 显示器='27-inch 4K'} 办公电脑: 电脑{CPU='Intel i5', RAM='16GB', 储存='512GB SSD', 显卡='null', 显示器='24-inch 1080p'} 出现异常: 组装电脑,CPU和RAM是必需的。
|
总结
建造者模式是一种非常有用的创建型设计模式,可以帮助我们构建复杂的对象,并控制对象的构建过程。 它可以提高代码的可读性、可维护性和可扩展性。 在实际开发中,我们可以根据具体情况选择是否使用建造者模式,以及如何使用它。