重修设计模式-创建型-建造者模式

重修设计模式-创建型-建造者模式

允许用户通过链式调用方法来逐步构建复杂对象,让复杂对象的构建与它的表示分离,即对象的表示和对象的构造过程解耦。

建造者模式的原理和实现非常简单,重点在于复杂对象的构建过程和定制化。具体实现中,通常会定义一个 Builder 类,它拥有产品类的所有属性,并定义一系列链式调用的 set 方法,用于修改产品类属性,最后通过 build() 方法集中校验并创建产品类对象。

举个例子,Android 中 AlertDialog 是一个确认弹窗,其中有这样几个属性:

  • title:标题,必填。
  • message:展示内容,选填。
  • positiveButton:确认按钮文字,选填。
  • positiveButtonClick:确认按钮点击事件,选填,如果 positiveButton 有值则为必填。
  • cancelable:是否可取消,选填。

通过建造者模式实现如下:

kotlin 复制代码
class CAlertDialog private constructor(builder: Builder) {
    private val title: String = builder.title
    private val message: String? = builder.message
    private val positiveButton: String? = builder.positiveButton
    private val positiveButtonClick: OnClickListener? = builder.positiveButtonClick
    private val cancelable: Boolean = builder.cancelable

    fun show() {
        println("CAlertDialog(title=$title, message=$message, positiveButton=$positiveButton, positiveButtonClick=$positiveButtonClick, cancelable=$cancelable)")
    }

    //定义建造者
    class Builder {
        var title: String = ""
        var message: String? = null
        var positiveButton: String? = null
        var positiveButtonClick: OnClickListener? = null
        var cancelable: Boolean = true

        fun setTitle(title: String): Builder {
            this.title = title
            return this
        }

        fun setMessage(message: String): Builder {
            this.message = message
            return this
        }

        //使用Kotlin apply函数优化代码
        fun setPositiveButton(text: String) = this.apply { this.positiveButton = text }

        fun setPositiveButtonClick(click: OnClickListener) =
            this.apply { this.positiveButtonClick = click }

        fun setCancelable(cancelable: Boolean) = this.apply { this.cancelable = cancelable }

        fun build(): CAlertDialog {
            //校验依赖关系
            if (positiveButton?.isNotEmpty() == true) {
                if (positiveButtonClick == null) throw Throwable("请设置点击事件!")
            }
            return CAlertDialog(this)
        }
    }
}

使用时:

kotlin 复制代码
val dialog = CAlertDialog.Builder()
    .setTitle("标题")
    .setMessage("消息")
    .setPositiveButton("确定")
    .setPositiveButtonClick { v -> println("确定事件") }
    .setCancelable(true)
    .build()
dialog.show()

可以看到调用处的代码非常简洁,可以通过链式调用配置自定义属性,并在 build 方法中校验和创建对象,还避免了对象创建中的中间状态。不过 CAlertDialog 中的属性在 Builder 又定义了一遍,造成了代码重复,这也是建造者模式的缺点。

这种方式和直接设置产品类的 set 方法差异在什么地方呢?下面通过 set 方式实现一下上面例子。

kotlin 复制代码
//必填属性通过构造方法传入,就不用校验了
class CAlertDialog constructor(private var title: String) {
    private var message: String? = null
    private var positiveButton: String? = null
    private var positiveButtonClick: OnClickListener? = null
    private var cancelable: Boolean = true

    fun setMessage(message: String) = this.apply { this.message = message }

    fun setPositiveButton(text: String) = this.apply { this.positiveButton = text }

    fun setPositiveButtonClick(click: OnClickListener) = this.apply { this.positiveButtonClick = click }

    fun setCancelable(cancelable: Boolean) = this.apply { this.cancelable = cancelable }

    fun show() {
        println("CAlertDialog(title=$title, message=$message, positiveButton=$positiveButton, positiveButtonClick=$positiveButtonClick, cancelable=$cancelable)")
    }
}

调用处:

kotlin 复制代码
val dialog = CAlertDialog1("标题")
    .setMessage("消息")
    .setPositiveButton("确定")
    .setPositiveButtonClick { v -> println("确定事件") }
    .setCancelable(true)
dialog.show()

这里为产品类的一系列 set 方法也增加了返回自身,达到链式调用的目的,调用同样简洁。但实现过程中属性间依赖关系无法统一校验了,且 set 方法一定是暴露的,就意味着外部能随时修改,不能达到创建后对象不可变的目的了。

建造者模式 VS set方式:

建造者模式:

  • 优点:
    • 支持链式调用
    • 支持统一校验,符合早抛出,晚捕获的异常处理原则(在发现错误时,应尽早抛出异常,避免错误扩散。有助于定位问题,提高程序的健壮性)
    • 支持对象创建后属性不可变
    • 一次性构建对象,避免无效的中间状态
  • 缺点:
    • 代码重复,产品类属性需要在建造者中重新定义一遍。
    • 如果产品类内部变化不大,使用建造者模式会增加复杂性。

set 方式:

  • 优点:
    • 支持链式调用
    • 实现简单,无重复代码
  • 缺点:
    • 不支持属性依赖关系的统一校验
    • 必填对象过多时,构造方法中参数冗长
    • 不支持不可变对象
    • 有短暂的中间状态

如果不在意对象是否可变和短暂无效状态,set 方式也是可以使用的,毕竟 Builder 中的重复代码和建造者对象也会造成一些损耗,具体还是要视需求复杂程度而定,在软件编码原则和需求契合度之间做折中。

建造者与工厂模式区别:

  • 建造者关注对象的创建过程,通过一系列复杂步骤创建对象,每个步骤可以独立的改变对象的状态;工厂模式关注对象的创建结果,隐藏对象的创建细节。
  • 建造者侧重对象的"定制化",工厂侧重对象的"规范化"

传统建造者模式

传统建造者模式是在链式调用基础上,通过模板模式进一步封装出指挥者(Director)、抽象建造者(Builder)和具体建造者(ConcreteBuilder),以便批量的创建出相同的复杂对象,这种方式封装程度更高,更加遵循设计模式的一些原则。不过任何设计模式都不能生搬硬套,还是那句话,在享受使用设计模式所带来的便捷性的同时,也不能被其所束缚。就建造者模式而言,在我客户端开发的生涯中很少遇到传统的实现方式,更多遇到的还是上面的 Builder + 链式调用的方式,比如 OkhttpClient 、AlertDialog 等。

相关推荐
4SAPI8 分钟前
Provider Adapter设计模式:如何让AI应用支持多个模型
网络·人工智能·设计模式
顽疲10 分钟前
Java vue 养老系统源码详解:入住核算单 live-check 缴费入账与预交金收据实践
java·vue.js·python·java 养老·springboot 养老·vue养老
共饮一杯无16 分钟前
LiveKit本地录制落盘路径排查与修复实践
java·go·livekit
StevenLdh28 分钟前
第八期 · 情绪闯关挑战盒:一个「无惩罚」成就系统的数据建模
java·微信小程序
Wang's Blog36 分钟前
Java 项目实战: 交友项目 - 入门案例:编写 Dubbo 服务消费者
java·dubbo·交友
IT研究室44 分钟前
最新计算机毕业设计选题推荐-基于spring boot的教育学习平台-网站-文档指导-Java-springboot
java·spring boot·课程设计
亚林瓜子1 小时前
Spring Boot 集成 ip2region 离线 IP 库实战指南
java·spring boot·后端·ip·ip2region
盛世宏博智慧档案1 小时前
恒湿净化一体机联动开发:档案馆温湿度阈值判断与启停控制实践
java·后端·struts·恒湿机·加湿除湿消毒净化一体机
vipxieliang1 小时前
ValidX 应用于 HR系统员工信息验证:入职、离职、社保数据
java·spring boot·后端
huaweichenai2 小时前
spring boot实现base64上传
java·spring boot·后端