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

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

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

建造者模式的原理和实现非常简单,重点在于复杂对象的构建过程和定制化。具体实现中,通常会定义一个 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 等。

相关推荐
进阶小白猿5 分钟前
Java技术八股学习Day17
java·jvm·学习
带刺的坐椅10 分钟前
从 Chat 到 Agent:Solon AI 带你进入“行动派”大模型时代
java·ai·agent·solon·mcp·java25
扶苏-su13 分钟前
Java--转换流-InputStreamReader 和 OutputStreamWriter
java·开发语言
我是小疯子6616 分钟前
深度学习实现智能文本摘要
java
heartbeat..31 分钟前
SQL 常用函数大全:聚合、字符串、数值、日期、窗口函数解析
java·数据库·sql·函数
老蒋每日coding1 小时前
AI智能体设计模式系列(一)—— 提示词链
设计模式·ai编程
袁慎建@ThoughtWorks1 小时前
ThreadLocal那些事儿
java·jdk·多线程·threadlocal
专注于大数据技术栈1 小时前
java学习--HashSet
java·学习·哈希算法
菜鸟233号1 小时前
力扣518 零钱兑换II java实现
java·数据结构·算法·leetcode·动态规划
扶苏-su1 小时前
Java--标准输入输出流
java·开发语言