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

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

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

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

相关推荐
奋进的芋圆1 小时前
Java 延时任务实现方案详解(适用于 Spring Boot 3)
java·spring boot·redis·rabbitmq
sxlishaobin1 小时前
设计模式之桥接模式
java·设计模式·桥接模式
model20051 小时前
alibaba linux3 系统盘网站迁移数据盘
java·服务器·前端
荒诞硬汉2 小时前
JavaBean相关补充
java·开发语言
提笔忘字的帝国2 小时前
【教程】macOS 如何完全卸载 Java 开发环境
java·开发语言·macos
2501_941882482 小时前
从灰度发布到流量切分的互联网工程语法控制与多语言实现实践思路随笔分享
java·开发语言
華勳全栈3 小时前
两天开发完成智能体平台
java·spring·go
alonewolf_993 小时前
Spring MVC重点功能底层源码深度解析
java·spring·mvc
沛沛老爹3 小时前
Java泛型擦除:原理、实践与应对策略
java·开发语言·人工智能·企业开发·发展趋势·技术原理
专注_每天进步一点点3 小时前
【java开发】写接口文档的札记
java·开发语言