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

文章目录

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

在这里插入图片描述

建造者模式概述

建造者模式是一种广泛使用的设计模式,在三方开源库和各种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;}@Overridepublic 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();@Overridepublic void buildWheel() {System.out.println("构建宝马的轮子");mCar.setWheel("宝马轮子");}@Overridepublic void buildBody() {System.out.println("构建宝马的车身");mCar.setBody("宝马车身");}@Overridepublic void buildEngine() {System.out.println("构建宝马的引擎");mCar.setEngine("宝马引擎");}@Overridepublic void buildColor() {System.out.println("构建宝马的颜色");mCar.setColor("黑色");}@Overridepublic void buildName() {System.out.println("构建宝马的名字");mCar.setName("宝马X5");}@Overridepublic Car getCar() {return mCar;}
}

奔驰车类:

public class BenzBuilder implements CarBuilder{private final Car mCar = new Car();@Overridepublic void buildWheel() {System.out.println("构建奔驰的轮子");mCar.setWheel("奔驰轮子");}@Overridepublic void buildBody() {System.out.println("构建奔驰的车身");mCar.setBody("奔驰车身");}@Overridepublic void buildEngine() {System.out.println("构建奔驰的引擎");mCar.setEngine("奔驰引擎");}@Overridepublic void buildColor() {System.out.println("构建奔驰的颜色");mCar.setColor("粉色");}@Overridepublic void buildName() {System.out.println("构建奔驰的名字");mCar.setName("奔驰");}@Overridepublic 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;}@Overridepublic 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) {// 构建一个PersonPersonB personB =  new PersonB.PersonBuilder("职场007","male").age(28).phone("1234567890").weight("83kg").build();System.out.println("构建者模式构建出的对象: " + personB);}
}

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

总结

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

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

搭建前后端的链接(java)

搭建前后端的链接(java) 一.前提 1.1 javaEE 搭建前后端的链接首先需要用到javaEE,也就是java企业版,也就是java后端(后端javaSE) 利用javaEE和前端交互,javaSE和数据库交互,javaSE和javaEE之间再进行交互就实现了前后端的交互…

【vim 学习系列文章 19 -- 映射快捷键调用两个函数 A 和B】

请阅读【嵌入式开发学习必备专栏 之 Vim】 文章目录 映射快捷键调用两个函数 映射快捷键调用两个函数 在 Vim 中,如果想通过按下 gcm 来调用两个函数,比如 FunctionA 和 FunctionB,需要先定义这两个函数,然后创建一个映射。这个映…

设计模式|单例模式(Singleton Pattern)

文章目录 适用场景优缺点优点缺点 示例分布式部署情况下,保证单例常见面试题 单例模式(Singleton Pattern)是一种创建型设计模式,它确保类只有一个实例,并提供一个全局访问点来访问这个实例。 适用场景 单例模式在许多…

2024-04-09 问AI: 在深度学习中,dropout层是什么?

文心一言 在深度学习中,Dropout层是一种正则化技术,用于防止神经网络过拟合。它通过在训练过程中随机地将一部分神经元的输出设置为0,来减少神经元之间的复杂共适应性,使得模型更加健壮,提高泛化能力。 具体来说&…

语音识别(录音与语音播报)

语音识别(录音与语音播报) 简介 语音识别人工智能技术的应用领域非常广泛,常见的应用系统有:语音输入系统,相对于键盘输入方法,它更符合人的日常习惯,也更自然、更高效;语音控制系…

JVM—垃圾收集器

JVM—垃圾收集器 什么是垃圾 没有被引用的对象就是垃圾。 怎么找到垃圾 引用计数法 当对象引用消失,对象就称为垃圾。 对象消失一个引用,计数减去一,当引用都消失了,计数就会变为0.此时这个对象就会变成垃圾。 在堆内存中主…

C++设计模式:抽象工厂模式(七)

1、定义与动机 抽象工厂定义:提供一个接口,让该接口负责创建一系列“相关或者相互依赖的对象”,无需指定它们具体的类动机: 在软件系统中,经常面临着“一系列相互依赖的对象”的创建工作;同时,…

人工智能——深度学习

4. 深度学习 4.1. 概念 深度学习是一种机器学习的分支,旨在通过构建和训练多层神经网络模型来实现数据的高级特征表达和复杂模式识别。与传统机器学习算法相比,深度学习具有以下特点: 多层表示学习:深度学习使用深层神经网络&a…

麒麟KOS删除鼠标右键新建菜单里不需要的选项

原文链接:麒麟KOS删除鼠标右键新建菜单里不需要的选项 Hello,大家好啊!在日常使用麒麟KOS操作系统时,我们可能会发现鼠标右键新建菜单里包含了一些不常用或者不需要的选项。这不仅影响我们的使用效率,也让菜单显得杂乱…

新能源电力行业设备点巡检系统的应用

新能源电力行业正日益成为全球能源结构的重要支柱,其设备点巡检系统作为确保电力设施安全、高效运行的关键环节,正受到业界的广泛关注和应用。 设备点巡检系统是一种集数据采集、实时监控、智能分析于一体的现代化管理工具。在新能源电力行业中&#xff…

Java常见算法_常见的查找算法和排序算法——简介及代码演示

在本文中我将介绍Java中的常见算法,查找算法包括基本查找、二分查找、插值查找和分块查找。排序算法包括冒泡排序、选择排序、插入排序和快速排序 查找算法: 1.基本查找: 代码: public class BasicSearchDemo {public static …

SpringMVC:搭建第一个web项目并配置视图解析器

👉需求:用spring mvc框架搭建web项目,通过配置视图解析器达到jsp页面不得直接访问,实现基本的输出“hello world”功能。👩‍💻👩‍💻👩‍💻 1 创建web项目 1…

Matplotlib之bar3d画3D柱状图

Matplotlib之bar3d画3D柱状图 一、参数x, y, z:array-likedx, dy, dz:float or array-likecolor:颜色顺序,可选zsort:字符,可选shade:bool, 默认Truelightsource:LightSourcedata:可索引对象,可选**kwargs二、代码3D柱状图通常用于展示数据集中不同分类项的数据大小或…

如何解决Python包管理问题:ERROR: Could not find a version that satisfies the requirement

如何解决Python包管理问题:“ERROR: Could not find a version that satisfies the requirement” 文章目录 如何解决Python包管理问题:“ERROR: Could not find a version that satisfies the requirement”错误描述问题分析解决方案检查包名确保网络连…

【JVM】面试题汇总

JVM1. 什么是JVM?2. 了解过字节码文件的组成吗?3. 什么是运行时数据区4. 哪些区域会出现内存溢出5. JVM在JDK6-8之间在内存区域上有什么不同 6. 类的生命周期 7. 什么是类加载器?类加载器有哪几种 8. 什么是双亲委派机制?有什么好…

深度学习框架中张量的执行过程

本文重点介绍深度学习框架OneFlow中张量执行背后发生的情况。以操作符oneflow.relu为例,介绍执行该操作符需要依赖的Interpreter和VM机制。希望本文对您对深度学习框架的系统设计有所启发。 首先,我们看一下PyTorch中的以下代码: import torch x = torch.tensor([-1.0, 2.0…

“国字号”荣誉、全国试点,侨乡群众身边的“放心”公证处

日前,我市五邑公证处获评“全国公共法律服务工作先进集体”称号。 走进公证处,首先映入眼帘的是一间宽敞明亮的大厅,办证点内还设置多个独立办证室,工作人员热情地为前来办理业务的市民提供专业、人性化的公证服务。江门市五邑公证…

Windows上面搭建Flutter Android运行环境

Flutter Android环境搭建 电脑上面安装配置JDK电脑上下载安装Android Studio电脑上面下载配置Flutter Sdk (避坑点一)下载SDK配置对应的环境变量 到path 电脑上配置Flutter国内镜像运行 flutter doctor命令检测环境是否配置成功创建运行Flutter项目&…

常见Linux嵌入式C语言笔试面试题

进程线程的基本概念 1.进程,线程概念,有什么区别 进程是计算机中运行的程序实例,它具有独立的内存空间、执行环境和系统资源。 线程是进程中的一个执行单元,多个线程共享同一进程的内存和资源。区别在于进程是资源分配的基本单位,而线程是处理器调度的基本单位 2.多进程、…

【牛客SQL快速入门】SQL基础(二)

一、高级查询 1. 计算函数 AVG AVG()为平均值函数,通过对表中行数计数并计算其列值之和,求得该列的平均值。 AVG() 可用来返回所有列的平均值,也可以用来返回特定列或行的平均值。 Select avg(gpa) From user_profile COUNT COUNT()函数…