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

文章目录

  • 建造者模式概述
  • 经典的建造者模式
  • 建造者模式的变种
  • 总结

在这里插入图片描述

建造者模式概述

建造者模式是一种广泛使用的设计模式,在三方开源库和各种SDK中经常见到。建造者设计模式在四人帮的经典著作《设计模式:可复用面向对象软件基础》中被提及,它的定义为,将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。建造者模式目前主要有两种,一种是经典的建造者模式,另外一种是变种的建造者模式。本文就是介绍建造者模式的两种形态的Java实现

经典的建造者模式

我们学过Java都知道,当我们需要一个对象的时候,直接new一个就行了,我们new对象的时候需要给其传递一些属性,最后就能得到一个可以使用的对象啦,比如构建一个简单的车轮子对象,我们只需要new 车轮子(属性)就行,但是当我们要构造一个车的对象时就没那么简单了,因为一辆车的这个大对象又包含了车轮子,引擎,车身等小对象,这些对象需要我们单独new出了,然后再组合到一起,最终形成一辆车。这个过程如果还是使用我们程序员手动new就会很麻烦了,所以出现了经典的建造者模式。

经典的建造者模式主要有四个参与者:

  1. 复杂对象:复杂对象就是被构造的的对象,例如一辆车:Car,实现抽象建造者接口的时候会引用这个对象,然后定义其构造的过程
  2. 抽象建造者接口: 定义创建复杂对象的各个组成部件的操作,例如CarBuilder,定义构建车的各个组成部件的操作
  3. 抽象建造者接口的具体实现 :可以定义多个,这个实现是实际构建复杂对象的地方,在这个类中会提供一个方法返回构建的复杂对象
  4. 抽象建造者接口的构造者和使用者 : 在这里面调用对应的建造者接口方法构造组装得到复杂对象。

接下来,我们使用一个造车的简单例子介绍经典的建造者模式
首先我们需要确定要构造的复杂对象,这里的复杂对象就是我们要造的车,我们定义一个类表示要造的车。

public class Car {
    private String wheel;
    private String body;

    private String engine;

    private String color;

    private String name;

    public Car() {

    }

    public String getWheel() {
        return wheel;
    }

    public void setWheel(String wheel) {
        this.wheel = wheel;
    }

    public String getBody() {
        return body;
    }

    public void setBody(String body) {
        this.body = body;
    }

    public String getEngine() {
        return engine;
    }

    public void setEngine(String engine) {
        this.engine = engine;
    }

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "Car{" +
                "wheel='" + wheel + '\'' +
                ", body='" + body + '\'' +
                ", engine='" + engine + '\'' +
                ", color='" + color + '\'' +
                ", name='" + name + '\'' +
                '}';
    }
}

接下来我们需要定义一个建造者接口,定义造车的各个操作,即定义抽象建造者接口。代码如下所示:

public interface CarBuilder {
     // 构建轮子
     void buildWheel();
     // 构建车身
     void buildBody();
     // 构建引擎
     void buildEngine();
     // 构建车身颜色
     void buildColor();
     // 构建名字
     void buildName();
     // 返回造好的车
     Car getCar();
}

定义好造车的操作后,我们就可以准备开始造车了,我们接下来分别定义造一辆奔驰和一辆宝马,这两种车的大致部件其实都差不多,无非都是轮子,车身,引擎,颜色,名字等,但是不同的就是各个部件中包含的技术,所以我们需要定义不同的类表示当前造的车,代码如下所示:
首先是宝马类:

public class BMWBuilder implements CarBuilder{
    private final Car mCar = new Car();
    @Override
    public void buildWheel() {
        System.out.println("构建宝马的轮子");
        mCar.setWheel("宝马轮子");
    }

    @Override
    public void buildBody() {
        System.out.println("构建宝马的车身");
        mCar.setBody("宝马车身");
    }

    @Override
    public void buildEngine() {
        System.out.println("构建宝马的引擎");
        mCar.setEngine("宝马引擎");
    }

    @Override
    public void buildColor() {
        System.out.println("构建宝马的颜色");
        mCar.setColor("黑色");

    }

    @Override
    public void buildName() {
        System.out.println("构建宝马的名字");
        mCar.setName("宝马X5");
    }

    @Override
    public Car getCar() {
        return mCar;
    }
}

奔驰车类:

public class BenzBuilder implements CarBuilder{
    private final Car mCar = new Car();
    @Override
    public void buildWheel() {
        System.out.println("构建奔驰的轮子");
        mCar.setWheel("奔驰轮子");
    }

    @Override
    public void buildBody() {
        System.out.println("构建奔驰的车身");
        mCar.setBody("奔驰车身");
    }

    @Override
    public void buildEngine() {
        System.out.println("构建奔驰的引擎");
        mCar.setEngine("奔驰引擎");
    }

    @Override
    public void buildColor() {
        System.out.println("构建奔驰的颜色");
        mCar.setColor("粉色");

    }

    @Override
    public void buildName() {
        System.out.println("构建奔驰的名字");
        mCar.setName("奔驰");
    }

    @Override
    public Car getCar() {
        return mCar;
    }
}

构造完上面的对象后,我们需要提供一个类来将这些构造的操作步骤组合到一起最终形成我们要构建的复杂对象,代码如下所示:

public class Director {
    private CarBuilder carBuilder;
    public Director(CarBuilder carBuilder){
        this.carBuilder = carBuilder;
    }

    public Car buildCar(){
        carBuilder.buildName();
        carBuilder.buildColor();
        carBuilder.buildWheel();
        carBuilder.buildBody();
        carBuilder.buildEngine();
        return carBuilder == null ? null : carBuilder.getCar();
    }
}

有了Director类,使用方只需要new一个Director对象,传入想要构建的车的Builder,调用buildCar方法就可以得到一个车的对象了。使用方法如下所示:

public class Client {
    public static void main(String[] args) {
        BMWBuilder bmwBuilder = new BMWBuilder();
        Director bmwDirector = new Director(bmwBuilder);
        Car bmwCar = bmwDirector.buildCar();
        System.out.println("构建了一辆宝马: " + bmwCar.toString());

        BenzBuilder benzBuilder = new BenzBuilder();
        Director benzDirector = new Director(benzBuilder);
        Car benzCar = benzDirector.buildCar();
        System.out.println("构建了一辆奔驰: " + benzCar.toString());
        }
}

运行结果:
在这里插入图片描述

建造者模式的变种

建造者模式的变种是目前用得比较多的,各大SDK初始化的时候基本都会使用建造者模式。经典的建造者模式重点在于抽象出对象创建的步骤,并通过调用不同的具体实现从而得到不同的结果。而变种的建造者模式目的在于减少对象在创建过程中引入的多个重载构造函数,可选参数以及Set方法的过度使用导致的不必要复杂性,变种的建造者模式更加适用于参数多的对象,并且这些参数中有部分参数是可选的,有部分参数是必传的。下面我们就以构建一个人(Person)的例子介绍变种的建造者模式。

public class Person {
    private final String mName; // 姓名,必填
    private final String mGender; // 性别,必填

    private final int mAge; // 年龄,可选

    private final String mPhoneNo; // 电话,可选

    private final String mWeight;// 体重,,可选


    public Person(String mName,
                  String mGender,
                  int mAge,
                  String mPhoneNo,
                  String mWeight) {
        this.mName = mName;
        this.mGender = mGender;
        this.mAge = mAge;
        this.mPhoneNo = mPhoneNo;
        this.mWeight = mWeight;
    }

    public Person(String mName,
                  String mGender,
                  int mAge,
                  String mPhoneNo) {
        this(mName, mGender, mAge, mPhoneNo, "");
    }

    public Person(String mName,
                  String mGender,
                  int mAge) {
        this(mName, mGender, mAge, "", "");
    }
}

在上面的类中,我们的姓名和性别是必选项,其他是可选项,为了能够实现必选和可选的功能,我们需要在类中定义多个重载构造函数,来满足我们的需求,或者是给提供set和get方法,让必选项的参数通过构造函数传递,但是参数很多的时候就会很复杂了,传递参数的时候特别头痛。所以出现了变种的建造者模式。代码如下:

public class PersonB {
    private final String mName; // 姓名
    private final String mGender; // 性别

    private final int mAge; // 年龄

    private final String mPhoneNo; // 电话

    private final String mWeight;// 体重

    private PersonB(PersonBuilder builder){
        mName = builder.name;
        mGender = builder.gender;
        mAge = builder.age;
        mPhoneNo = builder.phoneNo;
        mWeight = builder.weight;
    }

    public String getName() {
        return mName;
    }

    public String getGender() {
        return mGender;
    }

    public int getAge() {
        return mAge;
    }

    public String getPhoneNo() {
        return mPhoneNo;
    }

    public String getWeight() {
        return mWeight;
    }

    @Override
    public String toString() {
        return "PersonB{" +
                "mName='" + mName + '\'' +
                ", mGender='" + mGender + '\'' +
                ", mAge=" + mAge +
                ", mPhoneNo='" + mPhoneNo + '\'' +
                ", mWeight='" + mWeight + '\'' +
                '}';
    }

    public static class PersonBuilder {
        private final String name; // 姓名
        private final String gender; // 性别

        private int age; // 年龄

        private  String phoneNo; // 电话

        private String weight;// 体重

        public PersonBuilder(String name,String gender){
            this.name = name;
            this.gender = gender;
        }

        public PersonBuilder age(int age){
            this.age = age;
            return this;
        }

        public PersonBuilder phone(String phoneNo){
            this.phoneNo = phoneNo;
            return this;
        }

        public PersonBuilder weight(String weight){
            this.weight = weight;
            return this;
        }

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

观察上面的类我们可以发现,首先我们需要构造的对象PersonB的构造函数是私有的,意味着调用者无法直接通过实例化获取PersonB 的对象,其次PersonB类里面的属性是final的并且在构造函数中设置,对外只提供get方法,即调用者只能用,不能改。最后通过PersonBuilder的构造函数接收必选参数,其他的属性可以通过PersonBuilder提供的方法设置,其中每个方法都会返回当前的PersonBuilder对象,这样可以让我们设置参数的时候使用链式调用,如下所示:

  public PersonBuilder age(int age){
            this.age = age;
            return this;
        }

最后,我们可以看下如何使用变种的建造者模式:

public class Client {
    public static void main(String[] args) {
        // 构建一个Person
       PersonB personB =  new PersonB.PersonBuilder("职场007","male")
                .age(28)
                .phone("1234567890")
                .weight("83kg")
                .build();

        System.out.println("构建者模式构建出的对象: " + personB);
    }
}

运行结果:
在这里插入图片描述

总结

以上就是建造者模式的内容,本文分别介绍了两种形式的建造者模式以及Java的实现,其中用得最多的就是变种的建造者模式,建造者模式其实有个缺点就是需要编写很多的样板代码,但是我认为尽管这样还是不影响建造者模式的优雅,并且在Android Studio也有自动化生成变种建造者模式的插件,但是我不建议使用,除非读者已经对建造者模式很了解了。不然还是得仔细的看下建造者模式的设计思想,这样在看优秀库的源码时才会感觉到事半功倍。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/531863.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Flink常见面试问题(附答案)

目录 基础篇1. 什么是Apache Flink?2. Flink与Hadoop的区别是什么?3. Flink中的事件时间(Event Time)和处理时间(Processing Time)有什么区别?4. Flink的容错机制是如何实现的?5. 什…

配置交换机 SSH 管理和端口安全

实验1:配置交换机基本安全和 SSH管理 1、实验目的 通过本实验可以掌握: 交换机基本安全配置。SSH 的工作原理和 SSH服务端和客户端的配置。 2、实验拓扑 交换机基本安全和 SSH管理实验拓扑如图所示。 3、实验步骤 (1)配置交换机S1 Swit…

力扣HOT100 - 238. 除自身以外数组的乘积

解题思路: 当前位置的结果就是它左部分的乘积再乘以它右部分的乘积。因此需要进行两次遍历,第一次遍历用于求左部分的乘积,第二次遍历在求右部分的乘积的同时,再将最后的计算结果一起求出来。 class Solution {public int[] prod…

ClickHouse 介绍

前言 一个通用系统意味着更广泛的适用性,但通用的另一种解释是平庸,因为它无法在所有场景内都做到极致。 ClickHouse 在没有像三驾马车这样的指导性论文的背景下,通过针对特定场景的极致优化,获得闪电般的查询性能。 ClickHous…

计算机组成结构—外部存储器

目录 一、磁盘存储器 1. 磁表面存储器和磁记录原理 2. 硬磁盘的分类和基本结构 (1)硬磁盘存储器的分类 (2)硬磁盘存储器的组成 3. 磁盘的工作原理 (1)磁盘存储区域 (2)磁盘地…

备战蓝桥杯Day40 - 第11届python组真题 - C跑步锻炼

一、题目描述 二、思路 1、使用datetime库中的方法可以很好的解决这个问题。 2、定义起始时间和结束时间,判断是否是周一或者是1号,结果res加上相应的里程数。 3、最后输出 res 即为本题答案。 三、代码实现 import datetimestart datetime.date(2…

【LeetCode】手撕系列—82. 删除排序链表中的重复元素 II

目录 1- 思路2- 题解⭐删除排序链表中的重复元素 II——题解思路 3- ACM模式 原题链接:82. 删除排序链表中的重复元素 II 1- 思路 定义虚拟头结点 定义 cur 指针,cur指针始终指向虚拟头结点,依此操作 cur.next 和 cur.next.next while条件为…

centos7部署zabbix6.4.9

文章目录 [toc]一、环境准备1)部署lnmp2)修改配置文件3)安装数据库 二、部署zabbix1)下载zabbix2)安装zabbix服务端3)修改配置4)开机启动5)安装客户端 三、登录配置1)访问…

KVM+GFS分布式存储

本章内容: 学会KVMGFS分布式存储高可用 1.0 案例环境 1.案例环境 大规模使用 KVM 虚拟机来运行业务,为了保证公司虚拟机能够安全稳定运行, 决定采用 KVMGlusterFS 模式,来保证虚拟机存储的分布部署,以及分布冗余。避…

【技术揭秘】爬取网站或APP应用的几种常用方案:RPA、抓包工具、Python爬虫,你了解多少?

本来准备空闲之余尝试用RPA软件抓取数据,【AIRPA系列】1、利用AIRPA提升工作效率 应用场景 , 最近工作项目有点忙, RPA实操系列可能会晚点了(自己真正实操后再写,copy别人的没啥意思)。这里简单整理下爬取…

转让无区域资产管理公司要求和步骤

资产管理公司转让是指,一家资产管理公司将其管理的资产或资产组合转让给另一家资产管理公司或买家。这种转让通常是由于各种原因引起的,例如公司战略调整、市场需求变化或者是公司经营状况不佳等。在进行资产管理公司转让时,需要遵循一定的流…

python 08Pandas

1.基础概念 2.基本操作 (1)加载数据集 import pandas as pd #引入pandas包 打开csv文件 df pd.read_csv(./data/gapminder.tsv,sep\t) #\t制表符,即tab,缩进四个字符 \n表示回车换行 print(type(df)) print(df.head()) #…

vue的监视属性

目录 1. 场景引入2. watch3. 深度监视4. 监视属性简写5. 小结 1. 场景引入 在实际开发中,有时开发者需要根据某个属性的变化,做出相应的决策,因此Vue为开发者提供了watch.这一监视属性,用于实现此类需求。比如下面这个场景&…

解决 macOS 系统向日葵远程控制鼠标、键盘无法点击的问题

解决 macOS 系统向日葵远程控制鼠标\键盘无法点击的问题 1、首先正常配置,在系统偏好设置 - 安全性与隐私内,将屏幕录制、文件和文件夹、完全的磁盘访问权限、辅助功能全部都加入向日葵客户端 2、通过打开的文件访达,使用command shift G…

蓝桥杯2023A-05-互质数(Java)

5.互质数 题目描述 给定 a, b&#xff0c;求 1 ≤ x < a^b 中有多少个 x 与 a^b 互质。由于答案可能很大&#xff0c;你只需要输出答案对 998244353 取模的结果。 输入格式 输入一行包含两个整数分别表示 a, b&#xff0c;用一个空格分隔。 输出格式 输出一行包含一个…

java操作linux

文章目录 远程连接服务器执行linux命令或shell脚本介绍Process的方法相关类UML 工具类基本工具类依赖第三方的工具类 远程连接服务器 java程序远程linux服务器有两个框架分别是&#xff1a;jsch与ganymed-ssh2框架。推荐使用jsch框架&#xff0c;因为ganymed-ssh2框架不支持麒…

IO流的基础详解

文件【1】File类&#xff1a; 封装文件/目录的各种信息&#xff0c;对目录/文件进行操作&#xff0c;但是我们不可以获取到文件/目录中的内容。 【2】引入&#xff1a;IO流&#xff1a; I/O &#xff1a; Input/Output的缩写&#xff0c;用于处理设备之间的数据的传输。 【3】…

【单片机家电产品学习记录--蜂鸣器】

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 单片机家电产品–蜂鸣器 前言 记录学习单片机家电产品内容 已转载记录为主 一、知识点 1电子电路学习笔记&#xff08;17&#xff09;——蜂鸣器 蜂鸣器种类和原理 2疑…

【R基础】一组数据计算均值、方差与标准差方法及意义

【R基础】一组数据计算均值、方差与标准差方法及意义 均值、方差与标准差是用来描述数据分布情况 均值&#xff1a;用来衡量一组数据整体情况。 数据离散程度度量标准&#xff1a; 方差&#xff08;均方&#xff0c;s^2&#xff0c;总体参数&#xff0c;离均差平方和&#…

kotlin项目引用

概要&#xff1a; 记录项目引用kotlin具体事项 1 object下build.gradle buildscript {//声明引用版本ext.kotlin_version "1.4.20"repositories {google()mavenCentral()}dependencies {classpath "com.android.tools.build:gradle:4.2.0"//引用kotlinc…