【从零开始学设计模式】第六章_建造者模式

第六章_建造者模式

1.介绍

1.1定义

建造者模式,即==使用多个简单的对象一步一步构建成一个复杂的对象==

1.2解决的问题

主要解决在软件系统中,有时候面临着"一个复杂对象"的创建工作,其通常由各个部分的子对象用一定的算法构成;

1.3使用场景

1、需要生成的对象具有复杂的内部结构。 2、需要生成的对象内部属性本身相互依赖。3、建造者模式在创建复杂对象时非常有用,特别是当对象的构建过程涉及多个步骤或参数时。它可以提供更好的灵活性和可维护性,同时使得代码更加清晰可读。

1.4应用实例

1、去肯德基,汉堡、可乐、薯条、炸鸡翅等是不变的,而其组合是经常变化的,生成出所谓的"套餐"。

2、JAVA 中的 StringBuilder。

1.5角色

  • 产品(Product):表明需要构建的产品对象
  • 抽象建造者(Builder):抽象出来的构建者类,用于定==义创建对象所需的步骤==以及创建的步骤的调用过程
  • 具体建造者(ConcreteBuilder):抽象建造者的具体实现,对于不同的创建过程可以用不同的类进行实现
  • 指挥者(Director)**:使用 Builder 的类,提供给调用方**使用,调用方通过使用指挥者来获取产品

2.举例

2.1产品

java 复制代码
// 产品类,定义产品的三个部分
public class Product {
    private Object part1;
    private Object part2;
    private Object part3;

    public void setPart1(Object part1) {
        this.part1 = part1;
    }

    public void setPart2(Object part2) {
        this.part2 = part2;
    }

    public void setPart3(Object part3) {
        this.part3 = part3;
    }

    @Override
    public String toString() {
        return "Product{" +
                "part1=" + part1 +
                ", part2=" + part2 +
                ", part3=" + part3 +
                '}';
    }
}

2.2抽象建造者

满足多扩展、少实现的开发原则

java 复制代码
// 抽象建造者类,构建了一个产品对象,并定义了构建产品三个部分所需要的三个方法以及获取产品的方法
public abstract class Builder {
    protected Product product = new Product();

    public abstract void buildPart1();
    public abstract void buildPart2();
    public abstract void buildPart3();
    public abstract Product getProduct();
}

2.3具体建造者

java 复制代码
// 具体建造者 1
public class ConcreteBuilder1 extends Builder{
    @Override
    public void buildPart1() {
        product.setPart1("builder 1 set part 1.");
    }

    @Override
    public void buildPart2() {
        product.setPart2("builder 1 set part 2.");
    }

    @Override
    public void buildPart3() {
        product.setPart3("builder 1 set part 3.");
    }

    @Override
    public Product getProduct() {
        System.out.println("builder 1 build product.");
        return product;
    }
}

// 具体建造者 2
public class ConcreteBuilder2 extends Builder {
    @Override
    public void buildPart1() {
        product.setPart1("builder 2 set part 1.");
    }

    @Override
    public void buildPart2() {
        product.setPart2("builder 2 set part 2.");
    }

    @Override
    public void buildPart3() {
        product.setPart3("builder 2 set part 3.");
    }

    @Override
    public Product getProduct() {
        System.out.println("builder 2 build product.");
        return product;
    }
}

2.4指挥者对象

java 复制代码
// 指挥者对象
public class Director {
    private Builder builder;

    public Director(Builder builder) {
        this.builder = builder;
    }

    public Product construct() {
        builder.buildPart1();
        builder.buildPart2();
        builder.buildPart3();
        Product product = builder.getProduct();
        return product;
    }
}

2.5调用方

java 复制代码
public static void main(String[] args) {
    //调用方通过调用指挥者(传入的建造者不同,则创建的过程不一样)来创建产品,屏蔽了创建产品的过程
    Director director1 = new Director(new ConcreteBuilder1());
    Product product1 = director1.construct();
    System.out.println(product1);
    System.out.println("==================================");

    Director director2 = new Director(new ConcreteBuilder2());
    Product product2 = director2.construct();
    System.out.println(product2);
}

2.6测试结果

java 复制代码
builder 1 build product.
Product{part1=builder 1 set part 1., part2=builder 1 set part 2., part3=builder 1 set part 3.}
==================================
builder 2 build product.
Product{part1=builder 2 set part 1., part2=builder 2 set part 2., part3=builder 2 set part 3.}

我们可以看到通过给 Director 对象传入具体的构建者便能够构建出不同的产品对象,并且构建过程对于调用方来说是不可见的。

3.实例

假设我们要创建一个简单的汽车对象,包含品牌、颜色和引擎类型等属性,使用建造者模式建造该汽车对象

3.1产品

java 复制代码
public class Car {
    private String brand;
    private String color;
    private String engineType;

    public Car(String brand, String color, String engineType) {
        this.brand = brand;
        this.color = color;
        this.engineType = engineType;
    }

    // 省略 getter 和 setter 方法
}

3.2抽象建造者

java 复制代码
public abstract class CarBuilder {
    protected Car car;

    public void createCar() {
        car = new Car();
    }

    public abstract void buildBrand();
    public abstract void buildColor();
    public abstract void buildEngineType();

    public Car getCar() {
        return car;
    }
}

3.3具体建造者

java 复制代码
public class SedanCarBuilder extends CarBuilder {
    @Override
    public void buildBrand() {
        car.setBrand("Sedan");
    }

    @Override
    public void buildColor() {
        car.setColor("Red");
    }

    @Override
    public void buildEngineType() {
        car.setEngineType("Gasoline");
    }
}

public class SUVCarBuilder extends CarBuilder {
    @Override
    public void buildBrand() {
        car.setBrand("SUV");
    }

    @Override
    public void buildColor() {
        car.setColor("Blue");
    }

    @Override
    public void buildEngineType() {
        car.setEngineType("Diesel");
    }
}

3.4指挥者对象

java 复制代码
public class CarDirector {
    private CarBuilder carBuilder;

    public CarDirector(CarBuilder carBuilder) {
        this.carBuilder = carBuilder;
    }

    public void constructCar() {
        carBuilder.createCar();
        carBuilder.buildBrand();
        carBuilder.buildColor();
        carBuilder.buildEngineType();
    }

    public Car getCar() {
        return carBuilder.getCar();
    }
}

3.5调用方

java 复制代码
public class Main {
    public static void main(String[] args) {
        CarBuilder sedanCarBuilder = new SedanCarBuilder();
        CarDirector sedanCarDirector = new CarDirector(sedanCarBuilder);
        sedanCarDirector.constructCar();
        Car sedanCar = sedanCarDirector.getCar();
        System.out.println(sedanCar);

        CarBuilder suvCarBuilder = new SUVCarBuilder();
        CarDirector suvCarDirector = new CarDirector(suvCarBuilder);
        suvCarDirector.constructCar();
        Car suvCar = suvCarDirector.getCar();
        System.out.println(suvCar);
    }
}

3.6测试结果

java 复制代码
Car{brand='Sedan', color='Red', engineType='Gasoline'}
Car{brand='SUV', color='Blue', engineType='Diesel'}

通过抽象建造者(CarBuilder)声明了构建汽车的方法,并由具体建造者(SedanCarBuilder和SUVCarBuilder)实现这些方法。指挥者(CarDirector)负责指导具体建造者的建造过程。每个具体建造者都可以按照自己的方式构建汽车对象。这样,我们就可以灵活地创建不同类型的汽车对象。

4.源码应用

4.1StringBuilder类

JDK 中的建造者模式使用最多的就是 StringBuilder 类

StringBuilder 继承自 AbstractStringBuilder,而我们每次在调用 append 方法的时候就是在往 AbstractStringBuilder 类中变量 value 中追加字符

所以此时 AbstractStringBuilder 就对应抽象建造者StringBuilder 就是具体的建造者,String 对象就是我们所需要的产品。但是此时我们并没有发现 Director,其实此时的 StringBuilder 类同时也充当着 Director 的角色,其 toString() 方法就是返回最终 String 对象。

java 复制代码
abstract class AbstractStringBuilder implements Appendable, CharSequence {
    /**
     * The value is used for character storage.
     */
    char[] value;

    /**
     * The count is the number of characters used.
     */
    int count;
    
    ......
        
    @Override
    public AbstractStringBuilder append(char c) {
        ensureCapacityInternal(count + 1);
        value[count++] = c;
        return this;
    }
    ......
}
public final class StringBuilder
    extends AbstractStringBuilder
    implements java.io.Serializable, CharSequence
{
    ......

    @Override
    public StringBuilder append(char c) {
        super.append(c);
        return this;
    }

    }
    
    ......
    @Override
    public String toString() {
        // Create a copy, don't share the array
        return new String(value, 0, count);
    }
}

4.2Lombook中的@Bulider

我们在使用 Lombok 时有个基础注解叫 @Builder,使用该注解的类就不用再 new 对象,直接赋值然后调用 build() 方法便能构建对象。其原理就是在使用该注解的类中生成一个静态的内部 Builder类,然后通过调用该内部类的方法给生成的类对象赋值。我们以 Computer 类为例,代码如下:

java 复制代码
@Builder
public class Computer {
    private String CPU;
    private String GPU;
    private String memory;
    private String motherboard;
    private String hardDisk;

}

对代码进行编译后,再看最后生成的代码如下:

java 复制代码
public class Computer {
    private String CPU;
    private String GPU;
    private String memory;
    private String motherboard;
    private String hardDisk;

    Computer(String CPU, String GPU, String memory, String motherboard, String hardDisk) {
        this.CPU = CPU;
        this.GPU = GPU;
        this.memory = memory;
        this.motherboard = motherboard;
        this.hardDisk = hardDisk;
    }

    public static Computer.ComputerBuilder builder() {
        return new Computer.ComputerBuilder();
    }

    public static class ComputerBuilder {
        private String CPU;
        private String GPU;
        private String memory;
        private String motherboard;
        private String hardDisk;

        ComputerBuilder() {
        }

        public Computer.ComputerBuilder CPU(String CPU) {
            this.CPU = CPU;
            return this;
        }

        public Computer.ComputerBuilder GPU(String GPU) {
            this.GPU = GPU;
            return this;
        }

        public Computer.ComputerBuilder memory(String memory) {
            this.memory = memory;
            return this;
        }

        public Computer.ComputerBuilder motherboard(String motherboard) {
            this.motherboard = motherboard;
            return this;
        }

        public Computer.ComputerBuilder hardDisk(String hardDisk) {
            this.hardDisk = hardDisk;
            return this;
        }

        public Computer build() {
            return new Computer(this.CPU, this.GPU, this.memory, this.motherboard, this.hardDisk);
        }

        public String toString() {
            return "Computer.ComputerBuilder(CPU=" + this.CPU + ", GPU=" + this.GPU + ", memory=" + this.memory + ", motherboard=" + this.motherboard + ", hardDisk=" + this.hardDisk + ")";
        }
    }
}

可以看到反编译后内部生成了 ComputerBuilder 类,该类就是用于构建 Computer 对象,因此这也是一个构建者模式。

5.与工厂模式对比

建造者模式将对象的创建过程和表现分离,并且调用方通过指挥者调用方法对对象进行构建,使得调用方不再关心对象构建过程,构建对象的具体过程可以根据传入类型的不同而改变。通常在实际开发应用中通常会对建造者模式的角色进行阉割,往往只保留真正构建对象的过程。那么有的人就可能会有疑问了,构建者模式最终是获取一个对象,工厂模式也是获取一个对象,这两种模式有什么区别呢?这两种模式都是属于创建型模式,而这两种模式的侧重点不太一样:

1、对象类型:建造者模式用于复杂对象的构建,并且对象中的复杂组件的调用和赋值过程能够自定义;工厂模式创建的对象通常是具体类型或接口的实例

2、对象的组件:建造者模式在构建的过程需要知道对象需要哪些组件,并对组件进行赋值;工厂模式并不关心组件,直接调用方法即可

3、构建的过程顺序:从建造者模式的概念上讲,其实建造者模式是在一个**固定的流程下构建的对象,因此强调一定的执行顺序==**,而工厂模式并不关心。但是在开发中往往会忽略掉这部分的执行顺序

6.优缺点

优点:

  • 分离构建过程和表示,使得构建过程更加灵活,可以构建不同的表示。
  • 可以更好地控制构建过程,隐藏具体构建细节。
  • 代码复用性高,可以在不同的构建过程中重复使用相同的建造者。

缺点:

  • 如果产品的属性较少,建造者模式可能会导致代码冗余。
  • 建造者模式增加了系统的类和对象数量。

7.参考文章

https://blog.csdn.net/qq_38550836/article/details/125862850

相关推荐
gentle_ice38 分钟前
leetcode——矩阵置零(java)
java·算法·leetcode·矩阵
whisperrr.2 小时前
【JavaWeb06】Tomcat基础入门:架构理解与基本配置指南
java·架构·tomcat
火烧屁屁啦3 小时前
【JavaEE进阶】应用分层
java·前端·java-ee
m0_748257463 小时前
鸿蒙NEXT(五):鸿蒙版React Native架构浅析
java
我没想到原来他们都是一堆坏人3 小时前
2023年版本IDEA复制项目并修改端口号和运行内存
java·ide·intellij-idea
博一波4 小时前
【设计模式-行为型】迭代器模式
设计模式·迭代器模式
Suwg2094 小时前
【由浅入深认识Maven】第1部分 maven简介与核心概念
java·maven
花心蝴蝶.4 小时前
Spring MVC 综合案例
java·后端·spring
组合缺一7 小时前
Solon Cloud Gateway 开发:Helloword
java·gateway·solon