设计模式(java)- 建造者模式

2024-06-22 16:48

本文主要是介绍设计模式(java)- 建造者模式,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

1. 简介

  建造者模式,借用百度百科的解释:建造者模式是设计模式的一种,将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
  如上,可以看出来建造者模式属于创建型模式的一种,它的作用主要是:

  1. 将类对象的创建过程和细节封装起来,客户代码只需要知道自己建造对象的建造者是谁就可以获取到自己需要的类对象。
  2. 根据客户代码的需求可以通过相同的创建顺序和逻辑创建出拥有相同成员类型但是不同成员值的类对象。

2. 类图

  UML是帮助我们理解设计模式最好的工具,下面是一个基本的创建者模式的设计类图:
在这里插入图片描述
  如上对类图的各个模块进行说明:

  1. 指挥者类Direct,暴露给客户的类,客户创建一个对象时只需要与这个类交互就可以。
  2. 指挥类是提供给客户的接口类,指挥者中的接口一般是构建产品的方法,该方法的形参一般是具体的构建者对象。所以客户还需要关注的是构建者类(类似于工厂类),它就是客户的需求类,客户需要什么样的产品就传入对应的构建者类对象。
  3. 构建者类,拥有共同的抽象接口(IBuilder),根据不同的产品部件设计不同的创建者类(ConcretelBuilder)
  4. 产品类(Product),最终客户需要的产品对象,具体有构建者类进行构建。

3. 实例

3.1 场景

  一家手机代理制造商,自然有自己的手机制造工厂,类似于富士康。手机产品公司A要求代理商生产出如下的产品参数的手机。

CPU: Qualcomm Snapdragon 845
RAM: 8GB
ROM: 64GB
Battery: 3400mAh
Screen: Corning Gorilla Sixth Generation Screen
System: android8.1

3.2 代码实例

3.2.1 产品类

  根据如上的场景,我们可以设计一个手机产品类如下:

class EPhone {private String cpu;private String ram;private String rom;private String battery;private String screen;private String system;public EPhone() {this.cpu = "";this.ram = "";this.rom = "";this.battery = "";this.screen = "";this.system = "";}public void setCpu(String strCpu) {this.cpu = strCpu;}public void setRam(String strRam) {this.ram = strRam;}public void setRom(String strRom) {this.rom = strRom;}public  void setBattery(String strBattery) {this.battery = strBattery;}public void setScreen(String strScreen) {this.screen = strScreen;}public void setSystem(String strSystem) {this.system = strSystem;}public String getSystem() {return this.system;}//...
}

  该例子主要为了说明创建者模式,所以会省略一些代码。

3.2.1 创建者类

  有了产品类,那么需要一个建造者类,用于对产品的具体构建,这一步主要作用的封装产品的构建逻辑,以及根据产品需求不同的产品部件(部件类型相同,主要体现在部件的内部属性或逻辑),设计不同的建造类。
   根据以上的说明,建者者类有许多,所以需要抽象接口类或抽象类。

interface IPhoneBuilder {public void buildCpu();public void buildRam();public void buildRom();public void buildBattery();public void buildScreen();public void buildSystem();public EPhone createPhone();
}

  如上,建造者接口提供了建造产品各个部件的方法,以及最终生产出具体产品的方法:

public EPhone createPhone()

  下面代理商就需要根据A手机公司设计具体的建造者类了,定位该手机为A手机公司的高配版:

class EHighVersionPhoneBuilder implements IPhoneBuilder {private EPhone highPhone = null;public EHighVersionPhoneBuilder() {highPhone = new EPhone();}public void buildCpu() {highPhone.setCpu("Use Qualcomm Snapdragon 845");}public void buildRam() {highPhone.setRam("Use 8GB RAM");}public void buildRom() {highPhone.setRom("Use 64GB ROM");}public void buildBattery() {highPhone.setBattery("Use 3400 mAh battery");}public void buildScreen() {highPhone.setScreen("Use Corning Gorilla Sixth Generation Screen");}public void buildSystem() {highPhone.setSystem("Use android8.1 operating system");}public EPhone createPhone() {return highPhone;}
}

3.2.2 指挥类

  创建类设计完成后,客户代码本可以直接根据建造者类直接得到自己所需要的产品对象,但是我们会发现这个时候客户需要知道该产品的构建顺序是什么样的,那么客户就会写下如下的代码:

EHighVersionPhoneBuilder highVersionPhoneBuilder = new EHighVersionPhoneBuilder();
highVersionPhoneBuilder .buildCpu();
highVersionPhoneBuilder .buildRam();
highVersionPhoneBuilder .buildRom();
highVersionPhoneBuilder .buildBattery();
highVersionPhoneBuilder .buildScreen();
highVersionPhoneBuilder .buildSystem();
highVersionPhoneBuilder .createPhone();

  问题来了,这么多个接口方法,如果构建顺序发生变化,客户就需要跟着去修改代码,如果这块代码在很多处都有使用,那改起来很麻烦,也违背了开闭原则。所以这个时候我们需要一个指挥者类,来控制产品构建的顺序。

class EPhoneDirector {public static EPhone createPhone(IPhoneBuilder phoneBuilder) {if (phoneBuilder != null) {phoneBuilder.buildCpu();phoneBuilder.buildRam();phoneBuilder.buildRom();phoneBuilder.buildBattery();phoneBuilder.buildScreen();phoneBuilder.buildSystem();return phoneBuilder.createPhone();}return null;}
}

3.2.3 客户调用

  如下是客户创建一个高配版手机的过程:

public class EBuilder {public static void main(String[] args) {// TODO Auto-generated method stubEHighVersionPhoneBuilder highVersionPhoneBuilder = new EHighVersionPhoneBuilder();EPhone highPhone = EPhoneDirector.createPhone(highVersionPhoneBuilder);System.out.println(highPhone.getSystem()); }}

  我们会发现需要客户需要获取一个产品对象,只需要关注两个类,一个是指挥者类,一个是具体的建造类。

3.2.4 实例扩展

  至此,一个建造者模式的基本架构就出来了,那么当A公司又需要一个低配版的手机,怎么办呢,我们只需要拿到A公司提供的低配版参数,创建一个低配版的建造类就可以了。同理,A公司需要一个NFC版本的手机,其他参数和高配版一样,那么手机类新增一个NFC的设计方法,高配构建者新增一个nfc的构建接口,然后在指挥类加一个构建NFC高配版的方法就可以了:

class EPhoneDirector {public static EPhone createNfcPhone(IPhoneBuilder phoneBuilder) {if (phoneBuilder != null) {phoneBuilder.buildCpu();phoneBuilder.buildRam();phoneBuilder.buildRom();phoneBuilder.buildBattery();phoneBuilder.buildScreen();phoneBuilder.buildSystem();phoneBuilder.buildNfc();	return phoneBuilder.createPhone();}return null;}
}

  在这个建造者模式的架构中,我们可以根据具体需求灵活的使用,这样客户对产品的构建就会很方便,方便了代码的重用性。

4. 总结

4.1 优缺点

优点:

  1. 解耦,低耦合。客户代码不需要关注产品的创建逻辑和顺序。
  2. 扩展性好,根据客户的需求,可以扩展不同的建造者类或者根据产品创建的顺序的不同,新增不同的指挥方法,符合开闭原则。
  3. 便于控制内部细节和逻辑,便于维护。

缺点:

  1. 局限性:产品本身的局限性,一般需要构建的产品之间又相同或较多的共同点,产品的组成结构相同。这种局限性使得建造者模式的使用场景相对较少,当产品的差异性很大时,例如手机和电脑,这样则不适合使用该模式。
  2. 有类膨胀的理论风险:当产品类型很多时,我们就需要设计多个建造者类,这样会造成类过多的问题,该问题同样体现在工厂方法中。但是一班情况下不会出现这种情况。

  以上就是我对创建者模式的理解,创建者模式很少在项目中使用,但是如果遇到具体的业务,使用它还是会很方便我们程序的设计。总之具体问题具体分析,能够根据不同的业务场景来使用不同的设计模式,那就是好的设计。

设计模式代码:
https://github.com/eaikao/DesignPatterns.git

这篇关于设计模式(java)- 建造者模式的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1084852

相关文章

Java实现检查多个时间段是否有重合

《Java实现检查多个时间段是否有重合》这篇文章主要为大家详细介绍了如何使用Java实现检查多个时间段是否有重合,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录流程概述步骤详解China编程步骤1:定义时间段类步骤2:添加时间段步骤3:检查时间段是否有重合步骤4:输出结果示例代码结语作

Java中String字符串使用避坑指南

《Java中String字符串使用避坑指南》Java中的String字符串是我们日常编程中用得最多的类之一,看似简单的String使用,却隐藏着不少“坑”,如果不注意,可能会导致性能问题、意外的错误容... 目录8个避坑点如下:1. 字符串的不可变性:每次修改都创建新对象2. 使用 == 比较字符串,陷阱满

Java判断多个时间段是否重合的方法小结

《Java判断多个时间段是否重合的方法小结》这篇文章主要为大家详细介绍了Java中判断多个时间段是否重合的方法,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录判断多个时间段是否有间隔判断时间段集合是否与某时间段重合判断多个时间段是否有间隔实体类内容public class D

IDEA编译报错“java: 常量字符串过长”的原因及解决方法

《IDEA编译报错“java:常量字符串过长”的原因及解决方法》今天在开发过程中,由于尝试将一个文件的Base64字符串设置为常量,结果导致IDEA编译的时候出现了如下报错java:常量字符串过长,... 目录一、问题描述二、问题原因2.1 理论角度2.2 源码角度三、解决方案解决方案①:StringBui

Java覆盖第三方jar包中的某一个类的实现方法

《Java覆盖第三方jar包中的某一个类的实现方法》在我们日常的开发中,经常需要使用第三方的jar包,有时候我们会发现第三方的jar包中的某一个类有问题,或者我们需要定制化修改其中的逻辑,那么应该如何... 目录一、需求描述二、示例描述三、操作步骤四、验证结果五、实现原理一、需求描述需求描述如下:需要在

Java中ArrayList和LinkedList有什么区别举例详解

《Java中ArrayList和LinkedList有什么区别举例详解》:本文主要介绍Java中ArrayList和LinkedList区别的相关资料,包括数据结构特性、核心操作性能、内存与GC影... 目录一、底层数据结构二、核心操作性能对比三、内存与 GC 影响四、扩容机制五、线程安全与并发方案六、工程

JavaScript中的reduce方法执行过程、使用场景及进阶用法

《JavaScript中的reduce方法执行过程、使用场景及进阶用法》:本文主要介绍JavaScript中的reduce方法执行过程、使用场景及进阶用法的相关资料,reduce是JavaScri... 目录1. 什么是reduce2. reduce语法2.1 语法2.2 参数说明3. reduce执行过程

如何使用Java实现请求deepseek

《如何使用Java实现请求deepseek》这篇文章主要为大家详细介绍了如何使用Java实现请求deepseek功能,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录1.deepseek的api创建2.Java实现请求deepseek2.1 pom文件2.2 json转化文件2.2

Java调用DeepSeek API的最佳实践及详细代码示例

《Java调用DeepSeekAPI的最佳实践及详细代码示例》:本文主要介绍如何使用Java调用DeepSeekAPI,包括获取API密钥、添加HTTP客户端依赖、创建HTTP请求、处理响应、... 目录1. 获取API密钥2. 添加HTTP客户端依赖3. 创建HTTP请求4. 处理响应5. 错误处理6.

Spring AI集成DeepSeek的详细步骤

《SpringAI集成DeepSeek的详细步骤》DeepSeek作为一款卓越的国产AI模型,越来越多的公司考虑在自己的应用中集成,对于Java应用来说,我们可以借助SpringAI集成DeepSe... 目录DeepSeek 介绍Spring AI 是什么?1、环境准备2、构建项目2.1、pom依赖2.2