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

第六章_建造者模式

1.介绍

1.1定义

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

1.2解决的问题

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

1.3使用场景

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

1.4应用实例

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

2、JAVA 中的 StringBuilder。

1.5角色

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

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

2.举例

2.1产品

// 产品类,定义产品的三个部分
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抽象建造者

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

// 抽象建造者类,构建了一个产品对象,并定义了构建产品三个部分所需要的三个方法以及获取产品的方法
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具体建造者

// 具体建造者 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指挥者对象

// 指挥者对象
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调用方

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测试结果

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产品

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抽象建造者

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具体建造者

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指挥者对象

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调用方

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测试结果

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 对象。

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 类为例,代码如下:

@Builder
public class Computer {
    private String CPU;
    private String GPU;
    private String memory;
    private String motherboard;
    private String hardDisk;

}

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

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

你可能感兴趣的:(【从零开始学设计模式】,设计模式,建造者模式,java)