专栏首页Rust语言学习交流Rust 构建器(Builder)模式

Rust 构建器(Builder)模式

示例

通常在 Rust 中的实现是通过 不断重建 Builder 来构造最后的类型:

struct Counter {
    counted1: usize,
    counted2: usize,
    done: bool,
}

struct CounterBuilder {
    counted1: usize,
    counted2: usize,
}

impl CounterBuilder {
    // 构建器需要有默认的参数配置,然后从默认配置触发进行构建。
    // 不适用 #[derive(std::default::Default)],因为默认配置可能不一样
    fn default() -> Self {
        CounterBuiler {
            counted1: 5,
            counted2: 0,
        }
    }

    // 属性定制方法。消耗原本的构建器,修改属性后重新生成新构建器
    fn set_counted1(self, cnt: usize) -> Self {
        self.counted1 = cnt;
        self
    }

    fn set_counted2(self, cnt: usize) -> Self {
        self.counted2 = cnt;
        self
    }

    // 最后通过 `build` 方法生成所需类型
    fn build(self) -> Counter {
        Counter {
            counted1: self.counted1,
            counted2: self.counted2,
            done: false,
        }
    }
}

个人实践

在设置属性方法的时候,通常的实现是通过消耗原本的构造器后生成新构造器,这使得如果配置构造器的过程不能连续调用属性设置方法时,必须重新捕获构造器:

let mut builder = CounterBuilder::default();

// ... 进行一些计算,获得需要配置的值
let cnt1 = operations();

builder = builder.set_counted1(cnt);

// ... 进行一些计算,获得需要配置的值
let cnt2 = operations();

builder = builder.set_counted(cnt2);

以上代码通常出现在需要流计算并及时记录参数配置的时候。并且,如果构造器被更大型的数据结构持有时,消耗并重新构建构造器可能会对性能有点影响。因此在博主个人实现时通常采取传递&mut self 引用的方法来实现属性设置方法:

    // ...
    // 属性定制方法。消耗原本的构建器,修改属性后重新生成新构建器
    fn set_counted1(&mut self, cnt: usize) -> &mut Self {
        self.counted1 = cnt;
        self
    }

    fn set_counted2(&mut self, cnt: usize) -> &mut Self {
        self.counted2 = cnt;
        self
    }

// ...

改成如上形式的函数签名,即可 灵活构造 目标结构:

let mut builder = CounterBuilder::default();

// ... 进行一些计算,获得需要配置的值
let cnt1 = operations();

builder.set_counted1(cnt);

// ... 进行一些计算,获得需要配置的值
let cnt2 = operations();

builder.set_counted(cnt2);

// ... 可能还要等待别的操作完成后再进行构建

let counter = builder.build();

为什么使用构造器模式

  • 构造过程可控。通常实现构造器模式的时候,我们会将构造器所需要配置的属性设置为私有[^1],并且只能通过我们提供的属性设置方法进行设置,使得构造过程可控。另外,可以通过属性设置方法提前恐慌(panic)来阻止生成无效对象。
  • 设置方法职责专一。属性设置方法 [职责专一],只会负责设置一种属性,只有在该属性的设置规则改变时,相应的属性设置方法才需要进行修改;
  • 构造灵活。多个属性设置方法可以自由的组合在一起,也可以分步组合构造。
  • 可批量构造。我们除了使用消耗性的 build(self) 方法,也可以使用非消耗性的 fn build(&self) 方法,使得构造器可以多次复用。
  • 符合开闭原则。当某一属性的设置方法内部实现发生变化的时候,不影响其他属性的设置方式;而新增属性及其设置方法时,可以通过链式调用很方便地增加新属性的设置。

为什么不使用构造器模式

构造器模式由于有以下缺点而在部分场景中不适用:

  • 在构造完成前无法使用被构造对象。在构造完成之前,构造器并不生成被构造对象,因此在整个构造设置完成之前,无法使用被构造对象。
  • 构造器与被构造对象使用相同的属性设置方法,造成代码重复并无法复用。考虑需要只通过属性设置方法来修改对象的场景,当被构造对象在使用过程中需要频繁设置属性,那么就需要编写对应的属性设置方法;而如果还使用构造器进行对象构造,那么属性设置方法就会重复,并且可能造成构造器与被构造对象的属性设置行为不一致的问题[^2]。

参考资料

职责专一:https://baike.baidu.com/item/单一职责原则

[^1]: Rust 语言中默认语言项(Item)的可见性都是私有的,如需公开语言项给其他模块使用,需要使用 pub 关键字放开。[^2]: 一个绕开的行为不一致问题的方法是将属性设置规则抽取为静态函数,但仍然无法避免过度封装的问题。

本文分享自微信公众号 - Rust语言学习交流(rust-china)

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2020-07-12

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 【Rust问答】关于Deref, 使&&T变成&T的疑问

    目前知道rust会使&&&&T这种类型归一化, 使用时最终会变成一个&, 感觉这是deref在起作用, 所以翻看源码, 看到如下的系统实现:

    MikeLoveRust
  • 【翻译】200行代码讲透RUST FUTURES (6)

    让我们直接了当的说吧,Pin是这一系列概念中很难一开始就搞明白的,但是一旦你理解了其心智模型,就会觉得非常容易理解.

    MikeLoveRust
  • 【crossbeam系列】1有锁并发、无锁并发和crossbeam极简介

    随着计算机硬件和软件的发展,个人计算机里动辄几千几万线程已经成为家常便饭。而在程序中大量使用并发也成为了一个主流,因为这样的程序有更小的延迟,并且对多核CPU也...

    MikeLoveRust
  • 马云、李彦宏、贾跃亭、董明珠,科技大佬纷纷爱上造车,谁走得更顺?

    印象中已是第六次参加广州车展,今年的广州车展科技元素比往年多了不少,许多汽车品牌都在强调自己的科技元素,比如雷克萨斯的辅助驾驶功能,全新宝马 1系运动轿车强调互...

    罗超频道
  • SSE(Server-Sent Events):替代websocket完成服务器推送

    提到服务端数据推送,你可以一下子就想到了Websocket,WebSocket是一种全新的协议,随着HTML5草案的不断完善,越来越多的现代浏览器开始全面支持W...

    品茗IT
  • 姬小光前端小讲堂【第003期】

    在上一期的代码中,我们用到 h1,p,a 等标签,这些标签确定了我们想在网页中展示一个标题,一个段落,和一个链接。

    姬小光
  • 独家 | 手把手教你用Python构建你的第一个多标签图像分类模型(附案例)

    这让我思考——如果一个图像中有多个对象类别,我们该怎么办?制作一个图像分类模型是一个很好的开始,但我想扩展我的视野以承担一个更具挑战性的任务—构建一个多标签图像...

    数据派THU
  • 2019 Android 高级面试题总结 从java语言到AIDL使用与原理

    a.建造者模式: 将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。 使用场景比如最常见的AlertDialog,拿我们开发过程中举...

    Android技术干货分享
  • JMeter执行性能测试如何快速确定拐点

    1. 首先给大家介绍如何开发高效执行的性能测试脚本,目前多数用户都是分不同并发用户单次执行,该方法执行效率低,并且不方便数据比对,如下

    jmeter技术研究
  • 「性能测试实战30讲」之问题问答整理五

    第一个问题:如何理解“服务端的并发能力”这一描述? 首先我们从数据视角来理解,可以把服务端程序用一个模型来看待,即由「网络 API 请求」所驱动的。 服务端的...

    高楼Zee

扫码关注云+社区

领取腾讯云代金券