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

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构建器代码语法

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/348095.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

6000毫安以上智能手机_三星超长续航神机,6000毫安+128GB,上市半年不到跌至1499...

现在的手机是越来越智能了,无论是苹果还是安卓,基本都能为用户的生活添加几分乐趣,因为,当我们感到无聊时,基本都可以通过智能手机来打发时间。据我所知,不少人在用智能手机时都有个困扰,就是续…

使用RabbitMQ进行消息传递

RabbitMQ是一个强大的消息代理,可用于实现不同的消息传递模式。 即使有出色的教程 (使用不同的语言和框架),也很难理解这些概念。 在这篇文章中,我想展示一些可以用RabbitMQ实现的不同范例,以及为什么要为某…

android 为什么fragment在调用hide方法后没有生效_Android 多 Fragment 切换优化

code小生,一个专注 Android 领域的技术平台公众号回复 Android 加入我的安卓技术群作者:DDDong丶链接:https://www.jianshu.com/p/c8e8a0249911声明:本文已获DDDong丶授权发表,转发等请联系原作者授权问题分析一直在简书里看别人的…

mysql如何查看远程用户_MySQL系列(十)--用户权限及远程访问

本文基于MySQL8.0,记录一下完整的远程访问的过程,以及这个过程中可能遇到的问题,MySQL运行在阿里云服务器,操作系统:CentOS 7.6 64位顺便说下,买服务器还是要双十二这种拉新活动再买,用一个新的…

spring mvc拆分_Spring集成–强大的拆分器聚合器

spring mvc拆分健壮是什么意思? 在本文的上下文中,健壮性是指在不立即返回给调用者的情况下管理流中的异常条件的能力。 在某些处理方案中, n个 m个回答足以做出结论。 通常具有这些趋势的示例处理场景是: 财务,保…

mysql typeindex_explain mysql的type字段,索引的类型

4.type这列很重要,显示了连接使用了哪种类别,有无使用索引.从最好到最差的连接类型为const、eq_reg、ref、range、indexhe和ALL(1).system这是const联接类型的一个特例。表仅有一行满足条件.如下(t3表上的id是 primary key)mysql> explain select * from (select * from t3 …

JasperReports:棘手的部分

如果您使用Java进行编程的时间足够长,则有可能需要为业务用户生成报告。 就我而言,我已经看到几个项目使用JasperReports库来生成PDF和其他文件格式的报告。 最近,我荣幸地观察了Mike和他的团队使用上述报告库及其面临的挑战。 简而言之Jasp…

win mysql 2003错误_windows MySql 报1067错误 2003错误

1067错误原因是我把安装mysql的目录的名字改了。但是位于目录里面的my.ini配置文件没有修改,玛德我真傻逼。把my.ini的#Path to installation directory. All paths are usually resolved relative to this.basedir"D:/My_MySQL/"这下面一行的路径名改成目…

java heroku_Heroku和Java –从新手到初学者,第1部分

java heroku最近,我听说Heroku允许在Cedar堆栈中部署Java应用程序。 由于没有真正的软件构想,我决定尝试一下,仅配置SOMETHING以在Heroku上运行。 我对ReST有所了解(我仍然想学习并练习),所以我决定我的第…

mysql 合服_风云私服合区的方法详解(mysql数据库合并)

只能合并帐号,MY的库不能合并。倒数据前请留好备份,防止发生以外,破坏你得数据,切忌.1)首先确认你的机器上必须装MySQL 以及 mysql-connector-odbc-5.1.1-win32.msimysql-connector-odbc-5.1.1-win32.msi 就是 MySQL ODBC 5.1,你可…

mysql自定义数据类型_MySQL中的数据类型

一.MySQL中的数值类型分类MySQL中数值类型主要有三类:数字类型,字符串类型,时间类型。1.数字类型1.1整数tinyint 1字节smallint 2字节mediumint 3字节int 4字节bigint …

分布式系统开发注意点_分布式系统开发注意事项

分布式系统开发注意点开发分布式软件系统时,要考虑许多因素。 如果您甚至不知道第一句话中我在说什么,那么让我为您提供一些见解,示例以及有关分布式系统的实例。 总览 分布式系统是指多个物理硬件设备与单独的离散用户交互并通过这些硬件设…

String#repeat来到Java吗?

JDK-8197594 (“ String#repeat”)在其“描述”中包括以下内容:“长期以来,一直要求一种用于重复字符序列的String方法,没有后续操作。” 可以在JDK-8197594的“问题链接”中找到有关String类中用于重复字符…

mysql 什么是幻读_何为幻读?MySQL又是如何解决幻读的?

一、什么是幻读在一次事务里面,多次查询之后,查询的结果集的个数不一致的情况叫做幻读。而多出来或者少的哪一行被叫做 幻行二、为什么要解决幻读在高并发数据库系统中,需要保证事务与事务之间的隔离性,还有事务本身的一致性。三、…

电子电气架构——车载ECU刷写工具vFlash简介

电子电气架构——车载ECU刷写工具vFlash简介 我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 没有人关注你。也无需有人关注你。你必须承认自己的价值&a…

mysql执行系统命令_mysql 命令行执行 sql

1.直接输入sql执行MySQL> select now();---------------------| now() |---------------------| 2013-09-18 13:55:45 |---------------------1 row in set (0.00 sec)2.执行编写好的sql脚本mysql> source /home/1.sql---------------------| now() |-------------------…

jboss7.1.1 部署_在JBoss AS 7上部署BroadleafCommerce 2.0

jboss7.1.1 部署前2个步骤实际上与Broadleaf无关&#xff0c;但是提到该步骤是为了使其易于执行&#xff08;复制/粘贴&#xff09;这些步骤。 步骤&#xff03;1&#xff1a;在JBoss AS中配置数据源。 <datasource jta"true" jndi-name"java:jboss/datasou…

java中n次方怎么表示_java如何计算一个数的n次方

java递归算法&#xff0c;代码如下&#xff1a;public class Test3 { public double zhishu(double x,double y){ if(y>0){ return x*zhishu(x,y-1); }else if(y<0){ return (x*zhishu(x,-y-1)); }else{ return 1; } } public double fuzhishu(…

枚举集合的EnumSet

在上一篇博客文章中&#xff0c;我们发现了EnumMap用于带有枚举键的映射。 您可能已经观察到&#xff0c;还有一个专门针对枚举优化的Set &#xff1a; EnumSet 。 我们再次定义一个CoffeeType枚举&#xff1a; public enum CoffeeType {ESPRESSO, POUR_OVER, FRENCH_PRESS, …

linux下mkdir头文件_Linux中判断一个目录是否存在,如果不存在就创建这个目录

在操作文件目录时我们常常会考虑如下的功能&#xff1a;1、判断文件是否存在&#xff0c;并判断文件是否可写/目录是否存在Linux下&#xff1a;#includeint access(const char* pathname, int mode);参数介绍&#xff1a;返回值&#xff1a;成功0&#xff0c;失败-1pathname 是…