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

建造者模式

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

在这里插入图片描述

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

一、目的不同

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

二、创建对象的方式不同

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

三、适用场景不同

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

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

建造者模式最常见的应用场景之一是构建各类客户端。例如在进行第三方 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);
        }

        @Override
        public 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;

    @Generated
    LomBokMyClient(final String host, final Integer port, final Integer model) {
        this.host = host;
        this.port = port;
        this.model = model;
    }

    @Generated
    public static LomBokMyClient.LomBokMyClientBuilder builder() {
        return new LomBokMyClient.LomBokMyClientBuilder();
    }

    @Generated
    public String toString() {
        return "LomBokMyClient(host=" + this.host + ", port=" + this.port + ", model=" + this.model + ")";
    }

    @Generated
    public static class LomBokMyClientBuilder {
        @Generated
        private String host;
        @Generated
        private Integer port;
        @Generated
        private Integer model;

        @Generated
        LomBokMyClientBuilder() {
        }

        @Generated
        public LomBokMyClient.LomBokMyClientBuilder host(final String host) {
            this.host = host;
            return this;
        }

        @Generated
        public LomBokMyClient.LomBokMyClientBuilder port(final Integer port) {
            this.port = port;
            return this;
        }

        @Generated
        public LomBokMyClient.LomBokMyClientBuilder model(final Integer model) {
            this.model = model;
            return this;
        }

        @Generated
        public LomBokMyClient build() {
            return new LomBokMyClient(this.host, this.port, this.model);
        }

        @Generated
        public String toString() {
            return "LomBokMyClient.LomBokMyClientBuilder(host=" + this.host + ", port=" + this.port + ", model=" + this.model + ")";
        }
    }
}

总结

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

  • 17
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值