滴答滴:设计模式我是边学边记录,有问题的欢迎大家指出。学习的过程中会借助AI工具,我想说的是我们要把AI工具当作一个学识渊博的学者,同时要敢于质疑它,不能盲目的觉得对方说的一定是正确的,因为有时它的回答不见得是正确的,要带着自己的思考去使用AI工具,不断的和它对话和探讨。
一.什么是建造者模式
建造者模式是23种设计模式之一,是一种创建型设计模式,言而言之就是创建对象的模式。
你可能会说,java中创建对象直接new就可以了,是的,简单的可以直接new;对象有很多属性呢,直接在构造函数中写参数吗,5个参数以内是可以的,超过5个就违反编码规范了;每个属性的赋值过程比较复杂呢,每次都要在客户端写一遍吗?创建对象有很多步骤呢,每次也都要在客户端写一遍吗?等等,此时你还会说创建对象直接new就好了呀,这正式我们该思考的。
百度百科上是这样说的:建造者模式的核心思想是将一个”复杂对象的构建算法“与它的”部件“及”组装方式“解耦,使得构建算法和组装方式可以独立应对变化,复用同样的构建算法可以创建不同的表示,不同的构建过程可以复用相同的部件组装方式,下面我解释下这句话:
“复杂对象的构建算法”:这通常指的是一系列步骤,按照这些步骤,可以逐步构建一个复杂的对象。在建造者模式中,这些步骤被封装在建造者(Builder)接口或抽象类中,每个步骤对应一个方法。
“部件”:指的是构成复杂对象的各个组成部分。在建造者模式中,这些部件通常是对象的属性或子对象,它们可以通过建造者类(Builder)的方法进行设置。
“部件的组装方式”:这指的是如何将各个部件按照特定的顺序和规则组合成一个完整的对象。在建造者模式中,组装方式通常由指挥者(Director)类来负责。指挥者类知道如何调用建造者类的方法来逐步构建对象,并确定部件的组装顺序和规则。
通过解耦构建算法(即建造者类中的方法)和组装方式(即指挥者类的逻辑),我们可以更加灵活地构建和组装对象。当我们需要改变对象的构建方式时,只需要修改指挥者类中的逻辑,而不需要修改建造者类中的构建算法。同样地,当我们需要添加新的部件或修改部件的实现时,只需要修改建造者类中的相关方法,而不需要修改指挥者类中的组装方式。
这种解耦使得代码更加模块化、可维护和可扩展。同时,它也允许我们复用相同的构建算法来创建不同的对象表示,或者复用相同的部件组装方式来构建不同类型的对象。
建造者模式主要由五个部分组成:抽象产品,具体产品,抽象创建者,具体创建者,指挥者。代码示例用了百度百科的示例,如下:
// 产品,也可以只有具体产品,没有抽象产品
public class Person {
private String head;
private String body;
private String foot;
public String getHead() {
return head;
}
public void setHead(String head) {
this.head= head;
}
public String getBody() {
return body;
}
public String getFoot() {
return foot;
}
public void setFoot(String foot) {
this.foot= foot;
}
public void setBody(String body) {
this.body= body;
}
}
// Builder接口
public interface PersonBuilder{
void buildHead();
void buildBody();
void buildFoot();
Person buildPerson();
}
// Builder实现:普通人
publicclass HumanBuilder implements PersonBuilder {
private Person person;
public HumanBuilder(){
this.person=new Person();
}
public void buildBody() {
this.person.setBody("创建 人类 身体!");
}
publicvoid buildFoot() {
this.person.setFoot("创建 人类 脚!");
}
publicvoid buildHead() {
this.person.setHead("创建 人类 头!");
}
public Person buildPerson() {
return person;
}
}
// Builder实现:变形金刚
publicclass TransformerBuilder implements PersonBuilder {
private Person person;
public TransformerBuilder(){
this.person=newPerson();
}
publicvoid buildBody() {
this.person.setBody("创建 变形金刚 身体!");
}
public void buildFoot() {
this.person.setFoot("创建 变形金刚 脚!");
}
publicvoid buildHead() {
this.person.setHead("创建 变形金刚 头!");
}
public Person buildPerson() {
return person;
}
}
// Director构建向导
publicclass PersonDirector {
// 复杂对象的组装方式
public Person constructPerson(PersonBuilder pb) {
pb.buildHead();
pb.buildBody();
pb.buildFoot();
return pb.buildPerson();
}
}
// 测试
publicclass Test {
publicstaticvoid main(String[] args) {
PersonDirector pd = new PersonDirector();
Person person = pd.constructPerson(new HumanBuilder());
System.out.println(person.getBody());
System.out.println(person.getFoot());
System.out.println(person.getHead() +" \n");
Person transformer = pd.constructPerson(newTransformerBuilder());
System.out.println(transformer.getBody());
System.out.println(transformer.getFoot());
System.out.println(transformer.getHead());
}
}
二.建造者模式优缺点
1.优点
客户端不再负责对象的创建与组装,而是把这个对象创建的责任交给其具体的创建者类,把组装的责任交给组装类,客户端只负责对象的调用,从而明确了各个类的职责。
2.缺点
(1)虽然利用创建者模式可以创建出不同类型的产品,但是如果产品之间的差异巨大,则需要编写多个创建者类(具体创建者)才能实现,如果这样还是结合工厂模式更好。
(2)客户端无法灵活控制组件的细节,无法自己指定实体的属性值(依赖于具体创建者类重写抽象创建者接口中的方法)
扩展
针对缺点(2)可以将创建者类作为产品类的静态内部类,但有局限性,适用于以下场景:
- 当建造者和产品紧密关联时:如果建造者的唯一目的就是用来构建产品类的实例,并且它不会被其他类复用,那么将它作为产品类的静态内部类是一个很好的选择。
- 封装性要求高:如果你希望限制对建造者类的访问,仅允许产品类内部使用,那么静态内部类可以实现这一需求。
- 简洁性:如果你的产品类和建造者类逻辑相对简单,并且希望保持代码的简洁性,静态内部类可以减少文件数量和类之间的耦合。
- 只有一个具体创建者类
代码示例:
public class Car {
private Engine engine;
private Tires tires;
private Seats seats;
// 私有构造方法
private Car(Builder builder) {
this.engine = builder.engine;
this.tires = builder.tires;
this.seats = builder.seats;
}
// 静态内部类:建造者类
public static class Builder {
private Engine engine;
private Tires tires;
private Seats seats;
// 设置各个部分的配置(链式调用)
public Builder setEngine(Engine engine) {
this.engine = engine;
return this;
}
public Builder setTires(Tires tires) {
this.tires = tires;
return this;
}
public Builder setSeats(Seats seats) {
this.seats = seats;
return this;
}
// 构建Car对象
public Car build() {
return new Car(this);
}
}
// 省略getter方法...
// Engine, Tires, Seats 类的定义...
}
// 使用示例
public class CarExample {
public static void main(String[] args) {
Car car = new Car.Builder()
.setEngine(new Engine("V8"))
.setTires(new Tires("All-Season"))
.setSeats(new Seats("Leather"))
.build();
// 使用car对象...
}
}
三.建造者模式使用场景
1.创建复杂对象
例如上面”一.什么是建造者模式“中的讲解
2.初始化大量对象
例如扩展中的示例
// 创建一个配置好的建造者实例
Car.Builder baseConfigBuilder = new Car.Builder()
.setEngine(new Engine("V6"))
.setTires(new Tires("Summer"));
// 初始化多个对象
Car car1 = baseConfigBuilder.setSeats(new Seats("Fabric")).build();
Car car2 = baseConfigBuilder.setSeats(new Seats("Leather")).build();
// ...更多初始化操作
3.对象的创建过程需要进行多次配置
这已经在扩展示例中有所体现,通过链式调用的设置方法,可以在不同的步骤中配置对象的多个属性。请注意,这些示例是为了展示建造者模式在不同场景中的应用而简化的。在实际应用中,可能需要更复杂的类和逻辑。
4. 需要创建不可变对象
建造者模式通常与不可变对象一起使用,因为一旦对象被构建,其状态就不能再改变。在上面的扩展示例Car中,一旦build()
方法被调用,Car
对象的状态就不能再更改(假设Engine
、Tires
、Seats
也是不可变的)。
5.配置管理
在配置管理系统中,可以使用建造者模式来创建具有不同配置选项的对象。
// 假设有一个Configuration类,它包含了多个配置选项
public class Configuration {
// ...配置选项
// 静态内部类:建造者类
public static class Builder {
// ...配置选项的字段和设置方法
// 构建Configuration对象
public Configuration build() {
// 可能的验证逻辑...
return new Configuration(/* 使用字段值构建对象 */);
}
}
}
// 使用示例
public class ConfigurationManager {
public static void main(String[] args) {
Configuration config = new Configuration.Builder()
// 设置多个配置选项...
.build();
// 使用config对象...
}
}
四.建造者模式实现方式
此处借鉴博客:JAVA设计模式之建造者模式详解-CSDN博客
1.创建复杂对象
例如”一.什么是建造者模式“中讲解的示例
2.类构造器需要传入多个参数
例如”三.建造者使用场景“的2、3、4、5
解决如下问题:
(1)可以解决构造方法创建复杂对象的问题
构造方法如果参数过多,代码的可读性和易用性都会变差. 在使用构造函数时,很容易搞错
参数的顺序,传递进去错误的参数值,导致很有隐蔽的BUG出现。
(2)可以解决set方法创建复杂对象的问题
set方式设置对象属性时,存在中间状态
set方法还破坏了"不可变对象"的密闭性