当前位置: 首页 > news >正文

23种设计模式之建造者模式

文章目录

    • 建造者模式
    • 建造者模式与工厂模式有什么区别
    • 建造者模式使用场景 - 构建各类客户端
    • 建造者模式使用时机 - 多参数时适用
    • 为何多参数场景下选择建造者模式而非使用 set 方法?
    • 建造者模式简单实现 - 简单客户端
    • 创建建造者模式 - 注意的点
    • Lombok使用@Builder注解快速创建建造者模式
    • 总结

建造者模式

建造者模式将复杂对象构建与其表示分离。通过定义抽象建造者和具体建造者,以及指挥者协调构建过程,可精细控制对象创建。适用于创建复杂对象,提高代码可读性和可维护性,方便创建不同配置的对象。

在这里插入图片描述

建造者模式与工厂模式有什么区别

一、目的不同

  • 建造者模式:侧重于复杂对象的逐步构建,通过多个步骤来组装一个复杂对象,允许对象在构建过程中有不同的表示形式,更注重对象创建的过程控制。
  • 工厂模式:主要目的是根据不同的条件创建不同类型的对象,重点在于对象的创建结果,对创建过程的细节关注度相对较低。

二、创建对象的方式不同

  • 建造者模式:需要一个指挥者来协调具体建造者逐步构建对象,通常有多个方法参与对象的构建过程,并且可以对构建过程进行精细控制,比如先设置对象的某个属性,再设置另一个属性等。
  • 工厂模式:通常是通过一个工厂方法,根据传入的参数直接返回一个完整的对象,创建过程相对简单直接。

三、适用场景不同

  • 建造者模式:适用于创建复杂对象,该对象由多个部分组成,且构建过程较为复杂,需要逐步构建并可以灵活配置不同的部分。例如,创建一个具有多个可选配置的汽车对象,包括不同的发动机、内饰、颜色等。
  • 工厂模式:适用于对象的创建逻辑相对简单,主要根据不同的条件创建不同类型的对象。比如,根据用户的选择创建不同类型的图形对象(圆形、矩形、三角形等)。

建造者模式使用场景 - 构建各类客户端

建造者模式最常见的应用场景之一是构建各类客户端。例如在进行第三方 API 调用时,像亚马逊的 sp-api、简道云的 ApiClient 以及 SpringAI 的 ChatClient 等的构建,都采用的是建造者模式。这种模式能够更加灵活地配置和创建复杂的客户端对象。

建造者模式使用时机 - 多参数时适用

建造者模式并非在任何情况下都必须使用。主要需考量参数的数量。若仅有一两个参数,且组合方式也仅有一两种,那么此时无需使用建造者模式。然而,当参数数量达到十个左右时,其组合方式可能多达几百万种。在这种情况下,若使用构造器去创建实例,后果将难以想象。而建造者模式则能完美地解决这个问题,可实现任意组合,满足各种复杂的创建需求。

为何多参数场景下选择建造者模式而非使用 set 方法?

原因有以下三点:

  • 其一,set 方法通常是 public 对外暴露的,这可能导致对象状态被随意修改,降低了封装性。
  • 其二,使用 set 方法不方便添加参数校验逻辑。例如,设置 A 属性时校验需依赖 B 字段,这就对 set 的顺序有要求,而只有经验丰富的 “老司机” 才会清楚这些顺序要求,使用起来极为不便。
  • 其三,set 方法会破坏对象的 “不可变对象” 的密封性,使得对象在创建后可能被意外修改,影响程序的稳定性和可维护性。

相比之下,建造者模式在多参数场景下能够更好地控制对象的创建过程,确保对象的完整性和正确性。

建造者模式简单实现 - 简单客户端

建造者的一般格式有:

1、私有构造器
2、构造器传入建造器
3、建造器定义使用static修饰
4、属性值只需写在Builder 中
5、设置属性使用set方法,并返回this
6、Builder 使用build方法创建MyClient 对象,并把建造器设置到MyClient 中

public class MyClient {private Builder builder;// 私有构造方法,防止被实例化private MyClient(Builder builder) {this.builder = builder;}public void sendMessage(String message) {System.out.println("builder 信息:" + builder.toString());System.out.println("正在发送消息:" + message);}public static class Builder {private String host;private Integer port;private Integer model;Builder setHost(String host) {this.host = host;return this;}Builder setPort(Integer port) {this.port = port;return this;}Builder setModel(Integer model) {this.model = model;return this;}public MyClient build() {// 统一校验if (model == 0) {System.out.println("model 不能为0");return null;}// 其他校验return new MyClient(this);}@Overridepublic String toString() {return "Builder{" +"host='" + host + '\'' +", port=" + port +", model=" + model +'}';}}}

使用

     public static void main(String[] args) {MyClient myClient = new Builder().setHost("host").setPort(8080).setModel(1).build();myClient.sendMessage("test");}

执行结果:

builder 信息:Builder{host='host', port=8080, model=1}
正在发送消息:test

创建建造者模式 - 注意的点

1、设置属性不一定需要使用set作为方法前缀,可以不用set也可以使用其他词作为前缀,比如:

OpenAiChatOptions对象属性设置使用with作为前缀。

    @GetMapping("/chat/model/fc")String chatModelFC(@RequestParam(value = "message") String message) {ChatResponse response = chatModel.call(new Prompt(message,OpenAiChatOptions.builder().withFunction("lotteryFunction").withModel("gpt-3.5-turbo").withTemperature(0.4f).build()));return response.getResult().getOutput().getContent();}

Lombok使用@Builder注解快速创建建造者模式

在实体类上直接加@Builder注解。为了方便测试加了@ToString注解。

@ToString
@Builder
public class LomBokMyClient {private String host;private Integer port;private Integer model;}

测试使用

    public static void main(String[] args) {LomBokMyClient client = LomBokMyClient.builder().host("localhost").port(8080).model(1).build();System.out.println(client);}

执行结果

LomBokMyClient(host=localhost, port=8080, model=1)

下面是使用lombok生成的建造者模式的代码,也能用,但是字段存在了两份,会多占用内存一点。

public class LomBokMyClient {private String host;private Integer port;private Integer model;@GeneratedLomBokMyClient(final String host, final Integer port, final Integer model) {this.host = host;this.port = port;this.model = model;}@Generatedpublic static LomBokMyClient.LomBokMyClientBuilder builder() {return new LomBokMyClient.LomBokMyClientBuilder();}@Generatedpublic String toString() {return "LomBokMyClient(host=" + this.host + ", port=" + this.port + ", model=" + this.model + ")";}@Generatedpublic static class LomBokMyClientBuilder {@Generatedprivate String host;@Generatedprivate Integer port;@Generatedprivate Integer model;@GeneratedLomBokMyClientBuilder() {}@Generatedpublic LomBokMyClient.LomBokMyClientBuilder host(final String host) {this.host = host;return this;}@Generatedpublic LomBokMyClient.LomBokMyClientBuilder port(final Integer port) {this.port = port;return this;}@Generatedpublic LomBokMyClient.LomBokMyClientBuilder model(final Integer model) {this.model = model;return this;}@Generatedpublic LomBokMyClient build() {return new LomBokMyClient(this.host, this.port, this.model);}@Generatedpublic String toString() {return "LomBokMyClient.LomBokMyClientBuilder(host=" + this.host + ", port=" + this.port + ", model=" + this.model + ")";}}
}

总结

建造者模式将复杂对象构建过程分离,与工厂模式有不同用途。适用于多参数构建场景,相比 set 方法有诸多优势。可手动实现,也能用 Lombok 快速创建,为构建复杂对象提供了有效方式。


http://www.mrgr.cn/news/17757.html

相关文章:

  • HyperMesh教程从入门到精通:HyperMesh模型管理
  • 026、架构_资源_LoadServer
  • 在Android开发中,WiFi总是断开连接应该怎么办?
  • docker可用镜像源
  • PyTorch概述
  • iOS面试:使用系统的某些block api(如UIView的block版本写动画时),是否也考虑引用循环问题?
  • 【系统架构设计师】论文:论面向服务的架构设计及其应用
  • MFC生成dll的区别
  • 新版IDEA配置前进和后退、打开资源管理器等快捷按钮
  • 【JAVA开源】基于Vue和SpringBoot的新生报到网站
  • 基于STM32设计的防盗书包(华为云IOT)(216)
  • 【Spring】Spring MVC 入门(2)
  • P2024 [NOI2001] 食物链
  • Jenkins+docker+springboot 一键自动部署项目步骤
  • 网络编程核心函数
  • Linux系统练习笔记【完整版】
  • 一起学习LeetCode热题100道(66/100)
  • LIN总线CAPL函数—— 更新特定报文数据(linUpdateResponse)
  • GMeLLo:结合知识图谱的 LLM 多跳问答技术,效果显著提升
  • Java教程:入门基础【十万字详解】