目 录CONTENT

文章目录

建造者模式

小王同学
2023-12-24 / 0 评论 / 0 点赞 / 45 阅读 / 0 字

建造者模式

微信公众号:新时代程序猿
关注可了解更多的JAVA,PYTHON,ANDROID教程及开发技术。
问题或建议,请公众号留言;
如果你觉得文章对你有帮助,欢迎赞赏

建造者模式

前言

还是那句话,没有敲个几万行代码,谈设计模式都是瞎扯淡,咱先看看定义

在软件工程领域,设计模式是一套通用的可复用的解决方案,用来解决在软件设计过程中产生的通用问题。它不是一个可以直接转换成源代码的设计,只是一套在软件系统设计过程中程序员应该遵循的最佳实践准则。

所以说,没有工作经验或者没有项目经验,你会完全不知道设计模式在搞什么鬼~你就记住了,假如没有设计模式,软件照样开发,就是在大型软件系统开发及维护过程中就痛苦不堪,最后在不断重构后你会发现尼玛竟然用了好多设计模式。

建造者模式的定义和特点

定义虽然基本没有卵用,因为大部分人都看不懂,但是还的说出来

The intent of the Builder design pattern is to separate the construction of a complex object from its representation. By doing so the same construction process can create different representations.
将一个复杂对象的构建与其表示分离,使得同样的构建过程可以创建不同的表示

使用场景

设计模式的核心不在于你记住某一段代码,而是在于懂得某个设计模式的思想,知道在什么情况下使用什么设计模式,这样你的代码才能易于维护,下面是建造者设计模式的使用场景:

当一个类的构造函数参数个数超过4个,而且这些参数有些是可选的参数,考虑使用构造者模式。

解决的问题

当一个类的构造函数参数超过4个,而且这些参数有些是可选的时,我们通常有两种办法来构建它的对象。例如我们现在有如下一个类计算机类Computer,其中cpu与ram是必填参数,而其他3个是可选参数,那么我们如何构造这个类的实例呢,通常有两种常用的方式:

public class Computer {
    private String cpu;//芯片必须
    private String ram;//内存必须
    private int usbCount;//usb转换器可选
    private String keyboard;//键盘可选
    private String display;//显示器可选
}

第一:折叠构造函数模式(telescoping constructor pattern),这个我们经常用,如下代码所示

package designmode.factorypattern.buildpatter;

public class Computer {
    private String cpu;
    private String ram;
    private String mouse;
    private String keyboard;
    private String display;

    public Computer(String cpu, String ram) {
        this.cpu = cpu;
        this.ram = ram;
    }

    public Computer(String cpu, String ram, String mouse) {
        this.cpu = cpu;
        this.ram = ram;
        this.mouse = mouse;
    }

    public Computer(String cpu, String ram, String mouse, String keyboard) {
        this.cpu = cpu;
        this.ram = ram;
        this.mouse = mouse;
        this.keyboard = keyboard;
    }

    public Computer(String cpu, String ram, String keyboard, String display, String mouse) {
        this.cpu = cpu;
        this.ram = ram;
        this.keyboard = keyboard;
        this.display = display;
        this.mouse = mouse;
    }
}

第二种:Javabean 模式,如下所示

public class Computer {
        ...

   public String getCpu() {
        return cpu;
    }

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

    public String getRam() {
        return ram;
    }

    public void setRam(String ram) {
        this.ram = ram;
    }

    public String getMouse() {
        return mouse;
    }

    public void setMouse(String mouse) {
        this.mouse = mouse;
    }

    public String getKeyboard() {
        return keyboard;
    }

    public void setKeyboard(String keyboard) {
        this.keyboard = keyboard;
    }

    public String getDisplay() {
        return display;
    }

    public void setDisplay(String display) {
        this.display = display;
    }
}

那么这两种方式有什么弊端呢?

第一种在穿参数的时候需要一一对应,一两个参数还好,如果是七八个参数,想想都很烦,传错了还难定位原因,熟悉代码的时候也不方便。你可以想象一下,当你要调用一个类的构造函数时,你首先要决定使用哪一个,然后里面又是一堆参数,如果这些参数的类型很多又都一样,你还要搞清楚这些参数的含义,很容易就传混了。。。那酸爽谁用谁知道。

第二种方式在构建过程中对象的状态容易发生变化,造成错误。因为那个类中的属性是分步设置的,所以就容易出错。

为了解决这两个问题,builder设计模式就出现了。

建造者模式的结构与实现

  1. 在Computer 中创建一个静态内部类 Builder,然后将Computer 中的参数都复制到Builder类中。
  2. 在Computer中创建一个private的构造函数,参数为Builder类型
  3. 在Builder中创建一个public的构造函数,参数为Computer中必填的那些参数,cpu 和ram。
  4. 在Builder中创建设置函数,对Computer中那些可选参数进行赋值,返回值为Builder类型的实例
  5. 在Builder中创建一个build()方法,在其中构建Computer的实例并返回
public class Computer {
    private String cpu;
    private String hardDisk;
    private String mouse;
    private String display;
    private Computer(Builder builder){
        this.cpu=builder.cpu;
        this.hardDisk=builder.hardDisk;
        this.display=builder.display;
        this.mouse = builder.mouse;
    }

    public static class Builder{
        private String cpu;//cpu必须
        private String hardDisk;//硬盘必须
        private String display;//显示屏可选
        private String mouse;//鼠标可选

        public Builder(String cpu, String hardDisk) {
            this.cpu = cpu;
            this.hardDisk = hardDisk;
        }

        public String getCpu() {
            return cpu;
        }

        public String getHardDisk() {
            return hardDisk;
        }

        public String getDisplay() {
            return display;
        }

        public Builder setDisplay(String display) {
            this.display = display;
            return this;
        }

        public String getMouse() {
            return mouse;
        }

        public Builder setMouse(String mouse) {
            this.mouse = mouse;
            return this;
        }

        public Computer build(){
            return new Computer(this);
        }
    }

    @Override
    public String toString() {
        return "Computer{" +
                "cpu='" + cpu + '\'' +
                ", hardDisk='" + hardDisk + '\'' +
                ", mouse='" + mouse + '\'' +
                ", display='" + display + '\'' +
                '}';
    }
}

如何使用
在客户端使用链式调用,一步一步的把对象构建出来。

public class clint {
    public static void main(String[] args) {
        Computer computer=new Computer.Builder("intel i9","三星")
                .setDisplay("三星显示屏")
                .setMouse("雷蛇鼠标")
                .build();
        System.out.println(computer);
    }
}

输出:

Computer{cpu='intel i9', hardDisk='三星', mouse='雷蛇鼠标', display='三星显示屏'}

案例

构建者模式是一个非常实用而常见的创建类型的模式(creational design pattern),例如Android开发中图片处理框架Glide,网络请求框架Retrofit等都使用了此模式。

扩展

其实上面的内容是Builder在Java中一种简化的使用方式,经典的Builder 模式与其有一定的不同,如果没有兴趣的同学就可以不用往下读了。

传统Builder 模式

构建者模式UML图如下所示

如上图所示,builder模式有4个角色。

  1. 产品角色(Product):它是包含多个组成部件的复杂对象,由具体建造者来创建其各个零部件。
  2. 抽象建造者(Builder):它是一个包含创建产品各个子部件的抽象方法的接口,通常还包含一个返回复杂产品的方法 getResult()。
  3. 具体建造者(Concrete Builder):实现 Builder 接口,完成复杂产品的各个部件的具体创建方法。
  4. 指挥者(Director):它调用建造者对象中的部件构造与装配方法完成复杂对象的创建,在指挥者中不涉及具体产品的信息。

我们接下来将最开始的例子使用传统方式来实现一遍。

第一步:我们的目标Computer类:

public class ComputerV2 {
    private String cpu;
    private String hardDisk;
    private String mouse;
    private String display;


    public ComputerV2(String cpu, String hardDisk) {
        this.cpu = cpu;
        this.hardDisk = hardDisk;
    }

    public String getMouse() {
        return mouse;
    }

    public void setMouse(String mouse) {
        this.mouse = mouse;
    }

    public String getDisplay() {
        return display;
    }

    public void setDisplay(String display) {
        this.display = display;
    }

    @Override
    public String toString() {
        return "ComputerV2{" +
                "cpu='" + cpu + '\'' +
                ", hardDisk='" + hardDisk + '\'' +
                ", mouse='" + mouse + '\'' +
                ", display='" + display + '\'' +
                '}';
    }
}

第二步:抽象构建者类

public abstract class ComputerV2Builder {
    public abstract void setMouse();
    public abstract void setDisplay();
    public abstract Computer getComputer();
}

第三步:实体构建者类,我们可以根据要构建的产品种类产生多了实体构建者类,这里我们需要构建两种品牌的电脑,苹果电脑和联想电脑,所以我们生成了两个实体构建者类。

苹果电脑构建者类

public class AppleComputerBuilder extends ComputerV2Builder{
    private ComputerV2 computerV2;

    public AppleComputerBuilder(String cpu,String diskHard) {
        this.computerV2 = new ComputerV2(cpu,diskHard);
    }

    @Override
    public void setMouse() {
        computerV2.setMouse("苹果鼠标");
    }

    @Override
    public void setDisplay() {
        computerV2.setDisplay("苹果显示器");
    }

    @Override
    public ComputerV2 getComputer() {
        return computerV2;
    }
}

戴尔电脑构建者类

public class DaleComputerBuilder extends ComputerV2Builder{
    private ComputerV2 computerV2;

    public DaleComputerBuilder(String cpu, String diskHard) {
        this.computerV2 = new ComputerV2(cpu,diskHard);
    }

    @Override
    public void setMouse() {
        computerV2.setMouse("戴尔鼠标");
    }

    @Override
    public void setDisplay() {
        computerV2.setDisplay("戴尔显示器");
    }

    @Override
    public ComputerV2 getComputer() {
        return computerV2;
    }
}

第四步:指导者类(Director)

public class ComputerDirector {
    public void makeComputer(ComputerBuilder builder){
        builder.setUsbCount();
        builder.setDisplay();
        builder.setKeyboard();
    }
}

使用

  • 首先生成一个director ,
  • 然后生成一个目标builder,
  • 接着使用director组装builder,
  • 组装完毕后使用builder创建产品实例
ComputerDirector computerDirector = new ComputerDirector();
        AppleComputerBuilder appleComputerBuilder = new AppleComputerBuilder("最牛逼CPU", "超快的硬盘");
        computerDirector.makeComputer(appleComputerBuilder);
        ComputerV2 computer = appleComputerBuilder.getComputer();
        System.out.println(computer);

输出结果如下:

ComputerV2{cpu='最牛逼CPU', hardDisk='超快的硬盘', mouse='苹果鼠标', display='苹果显示器'}

可以看到,文章最开始的使用方式是传统builder模式的变种, 首先其省略了director 这个角色,将构建算法交给了client端,其次将builder 写到了要构建的产品类里面,最后采用了链式调用。

该模式的主要优点如下:

封装性好,构建和表示分离。
扩展性好,各个具体的建造者相互独立,有利于系统的解耦。
客户端不必知道产品内部组成的细节,建造者可以对创建过程逐步细化,而不对其它模块产生任何影响,便于控制细节风险。
其缺点如下:

产品的组成部分必须相同,这限制了其使用范围。
如果产品的内部变化复杂,或者产品内部发生变化,则建造者也要同步修改,后期维护成本较大。

0
  1. 支付宝打赏

    qrcode alipay
  2. 微信打赏

    qrcode weixin

评论区