fusion构建器代码语法

我发现构建器设计模式偶尔在代码中有用,但在测试中经常有用。 本文简要概述了该模式,然后介绍了在测试中使用该模式的一个有效示例。 请参阅github中的代码。

生成器模式的背景

根据GoF的书 ,构建器设计模式用于“将复杂对象的构造与其表示分离,以便同一构造过程可以创建不同的表示”。 像大多数GoF书中一样,这是一个准确而乏味的描述。

乔什·布洛赫(Josh Bloch)在他的《 有效的Java》一书中,为建筑商提出了一种更有趣的用法。 他的方法试图解决的问题是,当一个类具有“不止几个”参数时,这些参数通常是通过构造函数设置的,其中许多参数可能是可选的。 典型的解决方案是

  • 伸缩构造函数模式,在该模式中,您将为构造函数仅提供必需的参数,并为其他构造函数提供可选参数的变体,最终形成具有所有可选参数的构造函数。
    这行得通,但是导致一个相当混乱的解决方案,该解决方案可能包含大量构造函数来覆盖所有排列
  • 一个简单的构造函数(例如,仅用于必需的参数),由setter方法支持可选参数(JavaBeans方法)。 但是,这可能会使对象在构造过程中处于不一致状态,并且由于无法将字段定为final ,因此当然会阻止不变性。
  • 使用一个生成器。 这是Bloch建议的方法。 客户端创建一个生成器(通常使用无参数的构造器),然后在最终调用一个build方法之前,调用类似setter的方法来获取感兴趣的值(其余假定为默认值)。

几年前,我参加了一次演讲 ,其中Ted Young讨论了通过将构建器模式用于测试对象的构建,使构建器模式更进一步,下面将讨论这种方法。 [更新:请在此处查看Ted对这篇文章的回复]

使用Builder模式构建测试装置

使用Builder可以更轻松,更清晰地创建测试装置。

我通常使用此Builder方法进行测试的对象类型是域模型对象,例如Account,User,Widget或其他对象。 我支持使此类对象不可变 。
例如:

public final class Account {private final Integer id;private final String name;private final AccountType type;private final BigDecimal balance;private final DateTime openDate;private final Status status;public Account(Integer id, String name, AccountType type,BigDecimal balance, DateTime openDate, Status status) {this.id = id;this.name = name;this.type = type;this.balance = balance;this.openDate = openDate;this.status = status;}public Integer getId() {return id;}    //other getters, toString(), equals() and hashCode() omitted for brevity//no setters
}

使用这样的课程,您经常会遇到Bloch讨论的问题。 在此示例中,我们有一个强制您设置所有值的构造函数,但是我们也可以有很多变体,其中一些值可以省略以使用默认值。 因此,为测试创建这样一个类的实例可能会有些痛苦,如果它具有比这个简单示例更多的字段,则情况会更加痛苦。 您甚至必须为可能不需要测试的字段提供值。 这也使得很难知道哪些值实际上是测试所需要的,哪些值纯粹是为了编译。

建设者可以提供帮助。

public class AccountBuilder {//account fields with default valuesInteger id = 1;String name = "default account name";AccountType type = AccountType.CHECKING;BigDecimal balance = new BigDecimal(0);DateTime openDate = new DateTime(2013, 01, 01, 0, 0, 0);Status status = Status.ACTIVE;public AccountBuilder() {}public AccountBuilder withId(Integer id) {this.id = id;return this;}public AccountBuilder withName(String name) {this.name = name;return this;}public AccountBuilder withType(AccountType type) {this.type = type;return this;}public AccountBuilder withBalance(BigDecimal balance) {this.balance = balance;return this;}public AccountBuilder withOpenDate(DateTime openDate) {this.openDate = openDate;return this;}public AccountBuilder withStatus(Status status) {this.status = status;return this;}public Account build() {return new Account(id, name, type, balance, openDate, status);}
}

现在,您可以创建一个Account对象,以更轻松地进行测试。

关于使用Builder进行测试的注意事项

  • 默认值

构建器中使用的缺省值是避免出现异常的便利。 如果您的测试需要特定的测试值,则最好明确设置它们,而不要依赖于任何默认值。 它使您的测试意图更加清晰,并且在您需要更改默认值(例如由于业务需求变化)而使无意中止测试的风险降到最低时。

  • 非最终领域

域模型类本身是不可变的,因此具有最终字段。 根据设计,生成器中的所有字段都是非最终的。 因此,构建器不是线程安全的。 因此,请勿重复使用Builders; 而是为每个测试创建一个新实例。

  • 方法顺序不大

在大多数情况下,在构建器上调用方法的顺序应该不重要,并且在调用build()之前不会构造该对象。 这使构建器更易于使用,并避免了意外的意外。
此经验法则有明显且可以接受的例外。
例如打电话

Account account = new AccountBuilder().withType(AccountType.SAVING).withType(AccountType.CHECKING).build();

很傻,但被允许。 它只会为您提供类型检查的权限。
很好,但是请尝试避免引起更细微的混乱,例如,如果您的集合中可以添加某些内容,或者替换了整个集合(因此请清除以前的添加内容)。

使用Builder进行测试的优势

  • 易于阅读

以下声明不是特别清楚:

Account account = new Account(1, "test", 10, ...);

该声明更加清晰:

Account account = new AccountBuilder().withId(1).withName("test").withBalance(10).build();

正如Bloch所说,“ Builder模式模拟命名的可选参数”。

  • 仅指定与您的测试实际相关的值

如果您的测试仅涉及帐户余额和状态:

Account account = new AccountBuilder().withBalance(new BigDecimal(-100)).withStatus(Status.OVERDRAWN).build();

与必须在Accounts构造函数中指定每个值相反。

  • 创建无效对象的能力

域模型类的构造函数可能会(希望!)强迫您创建有效的对象。 在测试中,您可能要故意创建无效的对象以进行测试。

进一步的增强

便利方法

您可以为测试中使用的常见方案添加便捷方法。
例如

public AccountBuilder withNegativeBalance() {this.balance = new BigDecimal(-100);return this;}

灯具类

除了使用Builder类之外,我还发现拥有一个关联的Fixtures类很有用,该类提供用于测试的预构建实例。 这些可以利用Builder对象进行构造(尽管也没有什么可以阻止您使用原始构造函数)。
例如

public class AccountFixtures {//a shortcut to creating a basic Account objectpublic final Account ACCOUNT = new AccountBuilder().build();public final Account OVERDRAWN_CHECKING_ACCOUNT = new AccountBuilder().withType(AccountType.CHECKING).withNegativeBalance().build();public final Account CLOSED_SAVING_ACCOUNT = new AccountBuilder().withType(AccountType.SAVING).withZeroBalance().withStatus(Status.CLOSED).build();
}
参考: 构建器模式:很好的代码,非常适合我们的JCG合作伙伴 Shaun Abram的博客 Shaun Abram 进行测试 。

翻译自: https://www.javacodegeeks.com/2013/06/builder-pattern-good-for-code-great-for-tests.html

fusion构建器代码语法

fusion构建器代码语法_构建器模式:适用于代码,适用于测试相关推荐

  1. java类加载器 架构 设计_类加载器(DexClassLoader)与插件化(动态加载)

    类加载器与插件化解析 2.1 类装载器 DexClassLoader 首先,我们需要了解关于java代码本地import的一些知识: import中所引用的类有两个特点: 1.必须存在于本地,当程序运 ...

  2. 微信小程序构建商城百度云_构建云存储应用程序

    存档日期:2019年5月13日 | 首次发布:2015年2月13日 了解如何创建一个简单的Node.js应用程序,该应用程序使用IBM Object Storage Service for Bluem ...

  3. java代码识别_识别Java中的代码气味

    java代码识别 作为软件开发人员,我们不仅要编写有效的代码,而且还要编写可维护的代码,这是我们的责任. Martin Fowler在他的<重构:改进现有代码的设计>中将代码气味定义为: ...

  4. 存储过程可重用的代码块_如何使您的代码可重用

    可重用代码不是通用代码 Image Credits: Pixabay.com 可重用的代码作为解决所有软件问题的一站式解决方案,是一个危险的神话. 让我解释一下原因. 假设您正在编写软件库. 您脑中冒 ...

  5. 朴素贝叶斯分类器 文本分类_构建灾难响应的文本分类器

    朴素贝叶斯分类器 文本分类 背景 (Background) Following a disaster, typically you will get millions and millions of ...

  6. java构建内存池队列_内存池完整实现代码及一些思考

    为了提高效率和有效的监控内存的实时状态,我们采取了内存池的思想来解决效率与对内存实现监控的问题. 网上查找到了一些方案,根据自己的理解实现了应用. 我们什么时候要调用到内存池, 1,当我们频繁的申请释 ...

  7. python高阶函数闭包装饰器_Python_基础_(装饰器,*args,**kwargs,高阶函数,函数闭包,函数嵌套)...

    一,装饰器 装饰器:本质就是函数,功能是为其它的函数动态添加附加的功能 原则:对修改关闭对扩展开放 1.不修改被修饰函数的源代码 2.不修改被修改函数的调用方式 装饰器实现的知识储备:高阶函数,函数嵌 ...

  8. python 描述器 详解_描述器使用指南

    Python 的面向对象功能是在基于函数的环境构建的.通过使用非数据描述器,这两方面完成了无缝融合. Functions stored in class dictionaries get turned ...

  9. java 构建是什么意思_构建的概念

    [Maven 简介] Maven 是 Apache 软件基金会组织维护的一款自动化构建工具,专注服务于 Java 平台的项目构建和依赖管理.Maven 这个单词的本意是:专家,内行. Make-> ...

最新文章

  1. Centos6.5升级系统自带gcc4.4.7到gcc4.8.0
  2. 光纤通信及周边产品知识汇总
  3. React Native 的图片加载方式
  4. python 中用什么键缩进 —— tab 还是空格?
  5. Kubernetes stateful set讲解以及一个基于postgreSQL的具体例子
  6. TypeScript 常用的新玩法
  7. 【Python学习】 - sklearn - PCA降维相关
  8. gblfy大数据技术之安装CentOS系列
  9. 问一个AddDevice和设备符号链的问题??
  10. cocos2d-x JS 重力感应监听事件
  11. 【AI自学】 完备的 AI 学习路线,最详细的资源整理!
  12. python学习 (二十九) range函数
  13. 【资源】图深度学习文献列表
  14. JavaScript遍历DOM
  15. Java并发编程之安全发布对象的四种方法
  16. exfat最佳单元大小_回音壁构造和单元相关的个人看法
  17. win10鼠标停留任务栏不显示预览小窗口
  18. linux 修改文件日期,Linux系统如何修改文件的时间
  19. foxmail信纸设置html,教你如何设置Foxmail信纸花样?
  20. python0309

热门文章

  1. 2017西安交大ACM小学期数论 [阅兵式]
  2. Spring @Qualifier 注释
  3. Java中classLoader浅析
  4. Sentinel流控规则持久化
  5. mysql根据注释搜索表
  6. 第12步 用户模块前端(客户)
  7. linux写入二进制文件内容,linux – 从管道读取数据并写入标准输出,中间延迟.必须处理二进制文件...
  8. 转: databasemetadata 无法获取数据库表备注的解决方法
  9. 转:并发与并行的区别
  10. Makefile浅尝