创建型设计模式04-建造者模式

✨作者:猫十二懿

❤️‍🔥账号:CSDN掘金个人博客Github

🎉公众号:猫十二懿

建造者模式

1、建造者模式介绍

建造者模式是一种创建型设计模式,用于将一个复杂对象的构造过程与其表示分离开来,使得同样的构造过程可以创建不同的表示。该模式通过建造者类和指导者类的协作,将复杂的构造过程简化,并且增加了灵活性和可维护性。

1.1 建造者模式结构图

image-20230425222057889

  1. Builder:是为创建一个Product对象的各个部件指定的抽象接口
  2. ConcreteBuilder:是具体建造者,实现Builder接口,构造和装配各个部件。(实现具体的构建过程)
  3. Product:产品角色。
  4. Director:指挥者(指导者),是构建一个使用Builder接口的对象。

1.2 建造者模式基本代码

Product类:

/**
 * @author Shier
 * CreateTime 2023/4/25 22:26
 * 产品类
 */
public class Product {
    ArrayList<String>  parts = new ArrayList<>();

    /**
     * 添加新的产品部件
     * @param part
     */
    public void add(String part){
        parts.add(part);
    }

    /**
     * 列举所有的产品
     */
    public void show(){
        for (String part : parts) {
            System.out.println(part);
        }
    }
}

Builder 类

/**
 * @author Shier
 * CreateTime 2023/4/25 22:30
 * 抽象建造者类
 */
public abstract class Builder {
    /**
     * 建造部件A
     */
    public abstract void buildPartA();

    /**
     * 建造部件B
     */
    public abstract void buildPartB();

    /**
     * 获得产品建造后返回结果
     * @return
     */
    public abstract Product getResult(); 
}

ConcreteBuilderA 类:

/**
 * @author Shier
 * CreateTime 2023/4/25 22:33
 * 具体建造者A
 */
public class ConcreteBuilderA extends Builder{

    private Product product  =new Product();

    @Override
    public void buildPartA() {
        System.out.println("部件A");
    }

    @Override
    public void buildPartB() {
        System.out.println("部件B");
    }

    @Override
    public Product getResult() {
        return product;
    }
}

ConcreteBuilderB类与ConcreteBuilderA 类一样的,只是B的产品是C和D,不在重复写。

Director类:

/**
 * @author Shier
 * CreateTime 2023/4/25 22:36
 * 指导者类
 */
public class Director {
    public void construct(Builder builder){
        builder.buildPartA();
        builder.buildPartB();
    }
}

客户端类:

/**
 * @author Shier
 * CreateTime 2023/4/25 22:37
 * 客户端代码
 */
public class Client {
    public static void main(String[] args) {
        Director director = new Director();
        ConcreteBuilderA builderA = new ConcreteBuilderA();
        ConcreteBuilderB builderB = new ConcreteBuilderB();
        // 指导者用ConcreteBuilderA创建方法来建造产品
        director.construct(builderA); // A建造者创建的产品是A和B
        Product resultA = builderA.getResult();
        resultA.show();

        // 指导者用ConcreteBuilderB创建方法来建造产品
        director.construct(builderB);// B建造者创建的产品是C和D
        Product resultB = builderB.getResult();
        resultB.show();
    }
}

建造者模式是在当创建复杂对象的算法应该独立于该对象的组成部分以及它们的装配方式时适用的模式。

2、具体案例

画小人,要求是小人要有头、身体、两手、两脚就可以了。

2.1 不使用建造者模式-画小人

/**
 * @author Shier
 * CreateTime 2023/4/25 22:45
 */
public class Test extends JFrame {
    public Test() {
        setSize(400, 400);
        setDefaultCloseOperation(EXIT_ON_CLOSE);
        setLocationRelativeTo(null);
        setVisible(true);
    }

    public void paint(Graphics graphics) {
        //瘦小人
        graphics.drawOval(150, 120, 30, 30);   //头
        graphics.drawRect(160, 150, 10, 50);   //身体
        graphics.drawLine(160, 150, 140, 200); //左手
        graphics.drawLine(170, 150, 190, 200); //右手
        graphics.drawLine(160, 200, 145, 250); //左脚
        graphics.drawLine(170, 200, 185, 250); //右脚

        胖小人
        //graphics.drawOval(250, 120, 30, 30);   //头
        //graphics.drawOval(245, 150, 40, 50);   //身体
        //graphics.drawLine(250, 150, 230, 200); //左手
        //graphics.drawLine(280, 150, 300, 200); //右手
        //graphics.drawLine(260, 200, 245, 250); //左脚
        //graphics.drawLine(270, 200, 285, 250); //右脚
    }

    public static void main(String[] args) {
        new Test();
    }
}

如果在画一个胖小人,就是取消注释就可,但是有没有可能你复制别人的或者自己写的时候会漏写了代码,就有可能出现小人是缺胳膊少腿的情况。

就算将瘦小人和胖小人分别单独为一个类,但是这样还是避免不了缺胳膊少腿的情况呀。

所以说可以直接使用建造者模式,将两者普通同的部分抽取到抽象类当中

2.2 使用建造者模式-画小人

建造小人抽象类:

/**
 * @author Shier
 * CreateTime 2023/4/25 23:01
 * 建造小人抽象类
 */
public abstract class PersonBuilder {
    protected Graphics graphics;

    public PersonBuilder(Graphics graphics) {
        this.graphics = graphics;
    }
    // 头
    public abstract  void buildHead();
    // 身体
    public abstract  void buildBody();
    // 左手
    public abstract  void buildArmLeft();
    // 右手
    public abstract  void buildArmRight();
    // 右腿
    public abstract  void buildLegRight();
    // 左腿
    public abstract  void buildLegLeft();
}

胖小人类:

/**
 * @author Shier
 * CreateTime 2023/4/25 22:53
 * 胖小人
 */
public class PersonFatBuilder extends PersonBuilder {
    public PersonFatBuilder(Graphics graphics) {
        super(graphics);
    }

    /**
     * 头
     */
    @Override
    public void buildHead() {
        graphics.drawOval(250, 120, 30, 30);
    }

    /**
     * 身体
     */
    @Override
    public void buildBody() {
        graphics.drawOval(245, 150, 40, 50);
    }

    /**
     * 左手
     */
    @Override
    public void buildArmLeft() {
        graphics.drawLine(250, 150, 230, 200);
    }

    /**
     * 右手
     */
    @Override
    public void buildArmRight() {
        graphics.drawLine(280, 150, 300, 200);
    }

    /**
     * 右腿
     */
    @Override
    public void buildLegRight() {
        graphics.drawLine(270, 200, 285, 250);
    }

    /**
     * 左腿
     */
    @Override
    public void buildLegLeft() {
        graphics.drawLine(260, 200, 245, 250);
    }
}

瘦小人类:

/**
 * @author Shier
 * CreateTime 2023/4/25 22:53
 * 瘦小人
 */
public class PersonThinBuilder extends PersonBuilder {
    public PersonThinBuilder(Graphics graphics) {
        super(graphics);
    }

    /**
     * 头
     */
    @Override
    public void buildHead() {
        graphics.drawOval(150, 120, 30, 30);
    }

    /**
     * 身体
     */
    @Override
    public void buildBody() {
        graphics.drawRect(160, 150, 10, 50);
    }

    /**
     * 左手
     */
    @Override
    public void buildArmLeft() {
        graphics.drawLine(160, 150, 140, 200);
    }

    /**
     * 右手
     */
    @Override
    public void buildArmRight() {
        graphics.drawLine(170, 150, 190, 200);
    }

    /**
     * 右腿
     */
    @Override
    public void buildLegRight() {
        graphics.drawLine(170, 200, 185, 250);
    }

    /**
     * 左腿
     */
    @Override
    public void buildLegLeft() {
        graphics.drawLine(160, 200, 145, 250);
    }
}

建造人指挥类-指导建造怎么样的小人

指挥者(Director),用它来控制建造过程,也用它来隔离用户与建造过程的关联

/**
 * @author Shier
 * CreateTime 2023/4/25 23:08
 */
public class PersonDirector {
    private PersonBuilder personBuilder;

    /**
     * 初始化时指定要画什么样的人
     * @param personBuilder
     */
    public PersonDirector(PersonBuilder personBuilder) {
        this.personBuilder = personBuilder;
    }
    public void CreatePerson(){
        personBuilder.buildHead();
        personBuilder.buildBody();
        personBuilder.buildArmLeft();
        personBuilder.buildArmRight();
        personBuilder.buildLegRight();
        personBuilder.buildLegLeft();
    }
}

测试类:

/**
 * @author Shier
 * CreateTime 2023/4/25 22:45
 */
public class Test extends JFrame {
    public Test() {
        setSize(400, 400);
        setDefaultCloseOperation(EXIT_ON_CLOSE);
        setLocationRelativeTo(null);
        setVisible(true);
    }

    public void paint(Graphics graphics) {
        // 建造瘦小人
        PersonBuilder thinBuilder = new PersonThinBuilder(graphics);
        PersonDirector thinDirector = new PersonDirector(thinBuilder);
        thinDirector.CreatePerson();
        // 建造胖小人
        PersonFatBuilder fatBuilder = new PersonFatBuilder(graphics);
        PersonDirector fatDirector = new PersonDirector(fatBuilder);
        fatDirector.CreatePerson();
    }

    public static void main(String[] args) {
        new Test();
    }
}

在抽象类中创建了普遍存在的功能,只要继承抽象类就可实现复杂的构建过程,复杂构建过程的实现都在PersonBuilderA/B中实现了,表示在指挥者类实现

如果还有新的需求就是直接继承PersonBuilder类,让客户端调用实现即可。但是如果我要画人的五官,手指等人的细节的地方,又该如何实现?

如果这些细节是每个具体的小人都需要构建的,那就应该要加进去,反之就没必要。其实建造者模式是逐步建造产品的,所以建造者的Builder类里的那些建造方法必须要足够普遍,以便为各种类型的具体建造者构造。

2.3 电脑组件案例(简易完整版建造者模式)

我们要生产一台电脑,它具有CPU、显卡、内存等组件,每个组件都有不同的品牌、型号、参数等属性。我们可以使用建造者模式来实现其构建:

  1. 首先,需要定义电脑类Computer,它具有CPU、显卡、内存等组件属性;
  2. 然后,定义ComputerBuilder抽象类,其中包含设置CPU、设置显卡、设置内存等抽象方法,用于实现电脑组装过程的各个步骤;
  3. 接着,定义具体的电脑建造者类A、B、C等,分别实现ComputerBuilder中的抽象方法,构建不同的电脑配置;
  4. 最后,定义Director类,该类包含了构建电脑的方法buildComputer,其中封装了调用ComputerBuilder中各个步骤的顺序和逻辑,通过注入不同的具体建造者类,可以构建出不同的电脑对象。

这样,使用建造者模式可以将电脑的构建逻辑封装在具体建造者中,客户端代码仅需通过Director调用buildComputer方法来指定需要的电脑构建方式,从而实现了将复杂对象的构建与表示分离。它可以将对象的构建过程抽象化,使得相同的构建过程可以创建不同的表示。相比于直接在构造函数中设置可选参数,建造者模式使用setter的方式进行初始化,非常的灵活

那么看懂的要求,再来看看具体的代码实现吧

Computer类:也就是上面建造者模式的Product类

/**
 * @author Shier
 * CreateTime 2023/4/26 22:01
 */
public class Computer {
    private String cpu;
    private String gpu;
    private String  memory;

    public void setGpu(String gpu) {
        this.gpu = gpu;
    }

    public void setMemory(String  memory) {
        this.memory = memory;
    }

    public void setCpu(String cpu) {
        this.cpu = cpu;
    }

    @Override
    public String toString() {
        return "Computer:{" +
                "cpu='" + cpu + '\'' +
                ", gpu='" + gpu + '\'' +
                ", memory=" + memory +
                '}';
    }
}

ComputerBuilder类:

/**
 * @author Shier
 * CreateTime 2023/4/26 22:02
 * ComputerBuilder抽象类
 */
public abstract class ComputerBuilder {
    protected Computer computer;

    public Computer getComputer() {
        return computer;
    }

    /**
     * 构建Computer类
     */
    public void createNewComputer(){
        computer = new Computer();
    }
    public abstract void buildCpu();

    public abstract void buildGpu();

    public abstract void buildMemory();
}

三个具体的建造者类:每一个都实现ComputerBuilder中的方法,并定义自己的电脑配置

ComputerBuilderA:

/**
 * @author Shier
 * CreateTime 2023/4/26 22:05
 * ComputerBuilderA 具体建造者
 */
public class ComputerBuilderA extends ComputerBuilder{
    @Override
    public void buildCpu() {
        computer.setCpu("Intel i7-12500H");
    }

    @Override
    public void buildGpu() {
        computer.setGpu("NVIDIA GeForce RTX 1650");
    }

    @Override
    public void buildMemory() {
        computer.setMemory("16G");
    }
}

ComputerBuilderB:

/**
 * @author Shier
 * CreateTime 2023/4/26 22:05
 * ComputerBuilderB 具体建造者
 */
public class ComputerBuilderB extends ComputerBuilder{
    @Override
    public void buildCpu() {
        computer.setCpu("Intel i9 4090X");
    }

    @Override
    public void buildGpu() {
        computer.setGpu("NVIDIA GeForce RTX 4090");
    }

    @Override
    public void buildMemory() {
        computer.setMemory("64G");
    }
}

ComputerBuilderC:

/**
 * @author Shier
 * CreateTime 2023/4/26 22:05
 * ComputerBuilderC 具体建造者
 */
public class ComputerBuilderC extends ComputerBuilder{
    @Override
    public void buildCpu() {
        computer.setCpu("AMD Ryzen 9 5900X");
    }

    @Override
    public void buildGpu() {
        computer.setGpu("NVIDIA GeForce RTX 3080");
    }

    @Override
    public void buildMemory() {
        computer.setMemory("24G");
    }
}

Director类:

/**
 * @author Shier
 * CreateTime 2023/4/26 22:10
 * 创建Director类,用于指挥具体建造者类构建出特定的对象电脑对象:
 */
public class Director {
    private ComputerBuilder computerBuilder;
    public void setComputerBuilder(ComputerBuilder computerBuilder){
        this.computerBuilder = computerBuilder;
    }
    public Computer getComputer(){
        return computerBuilder.getComputer();
    }
    public void buildComputer(){ 
        computerBuilder.createNewComputer();
        computerBuilder.buildCpu();
        computerBuilder.buildGpu();
        computerBuilder.buildMemory();
    }
}

测试类:

/**
 * @author Shier
 * CreateTime 2023/4/26 22:13
 * main方法中使用Director构建具体的电脑对象
 */
public class ComputerTest {
    public static void main(String[] args) {
        // 创建Director类
        Director director = new Director();

        // 具体建造者A
        ComputerBuilderA builderA = new ComputerBuilderA();
        director.setComputerBuilder(builderA); // 让DIrector类构建A的电脑配置
        director.buildComputer(); // 在Director类中进行构建
        Computer computerA = director.getComputer();
        System.out.println("电脑A的配置:");
        System.out.println(computerA.toString());

        // 创建电脑建造者B
        ComputerBuilder builderB = new ComputerBuilderB();
        director.setComputerBuilder(builderB);
        director.buildComputer();
        Computer computerB = director.getComputer();
        System.out.println("电脑B的配置:");
        System.out.println(computerB.toString());

        // 创建电脑建造者C
        ComputerBuilder builderC = new ComputerBuilderC();
        director.setComputerBuilder(builderC);
        director.buildComputer();
        Computer computerC = director.getComputer();
        System.out.println("电脑C的配置:");
        System.out.println(computerC.toString());
    }
}

最后输出结果:

image-20230426222937681

这就是一个简单的建造者模式实现,通过不同的具体建造者类构建出了三个不同的电脑对象,并实现了将复杂对象的构建与表示分离。

3、总结

适用场景:

  • 建造者模式主要用于创建一些复杂的对象,这些对象内部子对象的建造顺序通常是稳定的,但每个子对象本身的构建通常面临着复杂的变化。
  • 建造者模式的好处就是使得建造代码与表示代码分离,由于建造者隐藏了该产品是如何组装的,所以若需要改变一个产品的内部表示,只需要再定义一个具体的建造者就可以了。

建造者模式优点:

  1. 可以将一个复杂对象的构造过程与其表示分离开来,并且可以使得同样的构造过程可以创建不同的表示。
  2. 建造者模式可以让客户端不必知道产品构建的细节,从而降低了系统的耦合度。
  3. 可以使用建造者模式逐步构建一个复杂的对象,从而可以控制对象构建的过程及其顺序。
  4. 可以隔离复杂对象的创建和使用,并且可以提高代码的复用性。

建造者模式缺点:

  1. 建造者模式需要为每一个需要创建的产品定义一个具体的建造者类,从而会增加系统中类的个数。
  2. 由于建造者模式要求建造者必须按照指定的顺序进行构造对象,因此可能会对其灵活性产生一定的影响。

em.out.println(“电脑C的配置:”);
System.out.println(computerC.toString());
}
}


最后输出结果:

[外链图片转存中...(img-i5ciGKgu-1685679055776)]

> 这就是一个简单的建造者模式实现,通过不同的具体建造者类构建出了三个不同的电脑对象,并实现了将复杂对象的构建与表示分离。



## 3、总结

适用场景:

- 建造者模式主要用于创建一些复杂的对象,这些对象内部子对象的建造顺序通常是稳定的,但每个子对象本身的构建通常面临着复杂的变化。
- 建造者模式的好处就是使得建造代码与表示代码分离,由于建造者隐藏了该产品是如何组装的,所以若需要改变一个产品的内部表示,只需要再定义一个具体的建造者就可以了。

建造者模式优点:

1. 可以将一个复杂对象的构造过程与其表示分离开来,并且可以使得同样的构造过程可以创建不同的表示。
2. 建造者模式可以让客户端不必知道产品构建的细节,从而降低了系统的耦合度。
3. 可以使用建造者模式逐步构建一个复杂的对象,从而可以控制对象构建的过程及其顺序。
4. 可以隔离复杂对象的创建和使用,并且可以提高代码的复用性。

建造者模式缺点:

1. 建造者模式需要为每一个需要创建的产品定义一个具体的建造者类,从而会增加系统中类的个数。
2. 由于建造者模式要求建造者必须按照指定的顺序进行构造对象,因此可能会对其灵活性产生一定的影响。







  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

猫十二懿

你的支持就是写文章的动力!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值