关于后端:设计模式之建造者模式

43次阅读

共计 6543 个字符,预计需要花费 17 分钟才能阅读完成。

本文由老王家组装电脑引出——建造者设计模式,具体介绍建造者模式的基本概念和实现代码,为了便于了解建造者模式,咱们会对理论利用中的典型案例进行介绍。最初比照工厂模式和建造者模式之间的区别,让咱们在理论应用时能更加灵便的抉择设计模式。

读者能够拉取残缺代码到本地进行学习,实现代码均测试通过后上传到码云。

一、引出问题

老王家须要组装一台笔记本电脑,然而就先买办公本还是游戏本的问题,老王和小王吵了起来。

因为如果两台电脑都要,那么洽购 CPU、内存 ……. 一系列配件不仅须要业余的常识,而且办公本和游戏本的配置也是不一样的,对于老王和小王来说,这都是事实的简单问题。就这样,他们从家一路吵到了电脑店 ……

售货员给他们进去一个主见,如果将配置电脑这个活交给一个业余的指挥者,而后让指挥者将洽购配件交给具体的游戏本和办公本的的洽购人员,这样你们只须要将须要的信息交给指挥者就行了,而无需关注洽购和组装过程。

这是老板又进去补充了一句,为了让指挥者不依赖具体的洽购人员,能够将洽购人员进一步形象进去。

二、模式概念与应用

实际上,下面波及到的问题的解决办法正是设计模式中的——建造者模式,也是创立型设计模式中的最初一个。

建造者模式将对象的创立过程和体现拆散,并且调用方通过指挥者调用办法对对象进行构建,使得调用方不再关怀对象构建过程,构建对象的具体过程能够依据传入类型的不同而扭转。

老王、小王就相当于客户端调用方,指挥洽购电脑的就是调用办法,他们的最终目标就是构建简单的对象(组装电脑),老王、小王只须要把相干信息交给指挥者,指挥者间接交给他成品,小王、老王无需关怀具体的细节。

在这个设计模式中包含四个角色:

产品、建造者、具体建造者、指挥者

在理论应用中为了简化也并不是四个角色都须要,往往只保留具体的构建过程。

咱们以老王组建电脑为例,看具体的实现代码:

产品类(电脑)

/**
 * 产品
 * @author tcy
 * @Date 30-07-2022
 */
public class Computer {
    private String CPU;
    private String GPU;
    private String memory;
    private String motherboard;
    private String hardDisk;


    public void setCPU(String CPU) {this.CPU = CPU;}

    public void setGPU(String GPU) {this.GPU = GPU;}

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

    public void setMotherboard(String motherboard) {this.motherboard = motherboard;}

    public void setHardDisk(String hardDisk) {this.hardDisk = hardDisk;}

    @Override
    public String toString() {
        return "you have a computer:\n" +
                "\t CPU:" + CPU + "\n" +
                "\t GPU:" + GPU + "\n" +
                "\t memory:" + memory + "\n" +
                "\t motherboard:" + motherboard + "\n" +
                "\t hardDisk:" + hardDisk + "\n";
    }
    Computer() {}

    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;
    }
    }

形象建造者:

/**
 * 形象建造者
 * @author tcy
 * @Date 30-07-2022
 */
public abstract class AbstractComputerBuilder {protected Computer computer = new Computer();

    public abstract void CPU();

    public abstract void GPU();

    public abstract void memory();

    public abstract void motherboard();

    public abstract void hardDisk();

    public abstract Computer getComputer();}

具体建造者 1(办公本组装者):

/**
 * 具体建造者 2
 * @author tcy
 * @Date 30-07-2022
 */
public class OfficeComputerBuilder extends AbstractComputerBuilder{
    @Override
    public void CPU() {computer.setCPU("i7-7700k");
    }

    @Override
    public void GPU() {computer.setGPU("GTX 1050 Ti");
    }

    @Override
    public void memory() {computer.setMemory("32GB");
    }

    @Override
    public void motherboard() {computer.setMotherboard("ASUS  B560M-PLUS");
    }

    @Override
    public void hardDisk() {computer.setHardDisk("1TB SSD");
    }

    @Override
    public Computer getComputer() {System.out.println("失去了一个办公电脑...");
        return computer;
    }
}

具体建造者 2(游戏本组装者):

/**
 * 具体建造者 1
 * @author tcy
 * @Date 30-07-2022
 */
public class GameComputerBuilder extends AbstractComputerBuilder{
    @Override
    public void CPU() {computer.setCPU("i9-12900K");
    }

    @Override
    public void GPU() {computer.setGPU("RTX 3090 Ti");
    }

    @Override
    public void memory() {computer.setMemory("64GB");
    }

    @Override
    public void motherboard() {computer.setMotherboard("Z590 AORUS MASTER");
    }

    @Override
    public void hardDisk() {computer.setHardDisk("2TB SSD");
    }

    @Override
    public Computer getComputer() {System.out.println("失去了一个游戏电脑...");
        return computer;
    }
}

指挥者:

/**
 * 指挥者
 * @author tcy
 * @Date 30-07-2022
 */
public class Director {
    private AbstractComputerBuilder builder;

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

    public Computer construct() {builder.CPU();
        builder.GPU();

        Computer product = builder.getComputer();
        return product;
    }
}

调用方(老王和小王):

/**
 * @author tcy
 * @Date 30-07-2022
 */
public class Client {public static void main(String[] args) {new Director(new GameComputerBuilder()).construct();

        new Director(new OfficeComputerBuilder()).construct();}

这样对于老王 (调用方) 来说,他须要办公本就间接将他须要办公本通知指挥者,指挥者去调用相应的采购员。老王无需晓得具体的洽购过程,小王也同样实用。

为了让读者了解的更加清晰,咱们以 Jdk、Mybatis、Spring 中的典型实用再做介绍和解说。

三、典型利用

1、Jdk 利用及 Lombok 利用

①StringBuilder 就是应用的建造者模式。

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

所以此时 AbstractStringBuilder 就对应形象建造者,StringBuilder 就是具体的建造者,String 对象就是咱们所须要的产品。

然而此时咱们并没有发现 Director,其实此时的 StringBuilder 类同时也充当着 Director 的角色,其 toString() 办法就是返回最终 String 对象。

②在咱们应用 Lombok 时在实体会加注解 @Builder。

在实体上加 @Builder 实际上生成了一个外部类,反编译后咱们看外部类的具体代码。

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() {}
    // 链式调用 ----------------start
    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;
    }
    // 链式调用 ----------------end
    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 + ")";
    }
}

动态外部类实际上充当建造者、指挥者的角色,创建对象时间接调用 实体.builder() 会生成该对象 而后调用 set 链式调用赋值。

Computer.ComputerBuilder computerBuilder=Computer.builder();
computerBuilder.CPU("it-9000")
        .memory("500m");

这就大大简化了对象的创立过程,还能够通过链式调用赋值。

2、Mybatis 中的利用

MyBatis 中的 SqlSessionFactoryBuilder 应用的建造者模式。

每个基于 MyBatis 的利用都是以一个 SqlSessionFactory 的实例为外围的。SqlSessionFactory 的实例能够通过 SqlSessionFactoryBuilder 取得。

而 SqlSessionFactoryBuilder 则能够从 XML 配置文件或一个事后定制的 Configuration 的实例构建出 SqlSessionFactory 的实例。

SqlSessionFactory 就是 Mybatis 须要的“产品”,SqlSessionFactoryBuilder 就是一个建造者,从 xml 配置文件或者 Configuration 中取出须要的信息形成不必的对象。

3、Spring 中的利用

Spring 中的 BeanDefinitionBuilder

BeanDefinition 是一个简单对象,通过 BeanDefinitionBuilder 来创立它。在启动过程中,会通过 BeanDefinitionBuilder 来一步步结构简单对象 BeanDefinition,而后通过 getBeanDefinition() 办法获取 BeanDefinition 对象。失去 BeanDefinition 后,将它注册到 IOC 容器中(寄存在 beanDefinitionMap 中)

BeanDefinition 就是须要的“产品”,BeanDefinitionBuilder 就是建设者。

四、总结

咱们能够看到,工厂模式和建造者模式用属于创立型设计模式,最终目标都是创建对象,那他们之间有什么区别呢? 在理论使用时又如何抉择呢?

其实比照看咱们在上篇文章、工厂模式的例子,咱们举的例子是老王购买产品 A、B、C 看名字就像是批量生产,而且咱们并没有说构建过程,就像是工厂生产产品一样。而咱们这篇文章举的例子却是电脑这么具体且简单的产品,且更重视每一步的组装过程,看到这咱们隐隐约约能感触到他们之间的区别。

①工厂模式创建对象无需分步骤,获取的产品对象齐全一样;而建造者模式会因为建造的程序不同,导致产出的产品不同(比方下面的 StringBuilder);

     ②建造者模式更适宜构建简单的对象,能够分步骤逐渐空虚产品个性,而工厂模式要求在创建对象的时候就须要把所有属性设置好;

如果只看概念性货色还是有些苍白无力,咱们举一个典型的 Spring 中的例子做比照。

Spring 中的 FactoryBean 接口用的就是工厂办法模式,FactoryBean 是一个工厂 bean,咱们能够通过实现 FactoryBean 接口并重写它的 getObject() 办法来自定义工厂 bean,并自定义咱们须要生成的 bean。

Spring 中本身就有很多 FactoryBean 的实现,他们暗藏了实例化一些简单 bean 的细节,调用者无需关注那些简单 bean 是如何创立的,只须要通过这个工厂 bean 来获取就行了!

而 BeanDefinition 是一个简单且高度个性化的一个 bean,外面有很多 Bean 的信息,例如类名、scope、属性、结构函数参数列表、依赖的 bean、是否是单例类、是否是懒加载等,其实就是将 Bean 的定义信息存储到这个 BeanDefinition 相应的属性中,创立过程应用建造者模式更适合。

联合典型利用,认真领会建造者模式和工厂模式区别,参考软件设计七大准则 在理论利用中更加灵便的应用,不生吞活剥。

这篇文章完结五种创立型模式就告一段落了。

一、设计模式概述

二、设计模式之工厂办法和形象工厂

三、设计模式之单例和原型

读者肯定要认真领会他们之间的区别,最好是把代码都写一遍增强了解。

正文完
 0