您的位置:首页 > 编程语言

实习日常(1)——提高代码质量:过度保护以及TDD,BDD

2017-01-02 21:16 183 查看

Beginning

TDD,BDD:在刚到公司的时候,重构java旧项目,花了漫长时间,写了与原代码运行路径一致,但很繁复的代码,导致在一天之后难以阐述逻辑。被质疑逻辑错误,当时委屈。最后修改抽出了一个子方法,并且将原if else语句进行了精简,代码逻辑清晰。当时看了一篇还不错的关于代码质量的文章事实上,TDD,BDD中提到的偶发复杂性(TDD遵循,仅仅编写足以使测试代码通过的代码,保持简单设计并适合目的)和代码质量导致的难以阅读是不同的,但是追求的易读性是共同的。

有时我们喜欢通过产出偶发复杂性(可以替换为简单设计的复杂设计)的设计来展示自己的精神能力,确使自己都理解不了那些设计。(——《有效的单元测试》)

认知超负荷是程序员特别常见的问题,因为编程本质上是繁重的记忆力活动。(——《有效的单元测试》)

TDD与BDD

1. 定义 (引自《有效的单元测试》——Lasse Koskela)

TDD

TDD(Test-Driven-Development, 测试驱动开发):在编写出能够证明代码存在的失败测试之前,不写生产代码。

优点:

代码变得可用——代码的设计和API适合于你的使用场景。

代码变得精益——生产代码仅仅实现场景所需要的功能(代码质量的罪魁祸首之一,以及使开发者生产力停滞的主要因素,就是称为偶发复杂性:即不必要的复杂性。可以通过替换为简单的设计来避免它,同时仍然满足需求。)

BDD

BDD(Behaviour-Driven-Development,行为驱动开发):是在TDD上发展衍生来的。TDD思想和词汇表中所讲的“测试”会误导人们。

作为BDD实践者,我们小心地以例子的形式编写验收测试,是任何团队成员都能够理解。我们遵循这个过程,编写例子来从业务干系人那里获得反馈,在动手之前就能了解我们是否在构建正确的东西。(——《Cucumber:行为驱动开发指南》)

也就是说。今天的BDD语境和领域远远超出了代码——最引人注目的是将BDD提升到需求层面,与业务分析和需求行为结合起来。

也就是说我们不是一定要用“测试”来作为工具(但并不是说测试不是TDD的本质——所产生的函数和方法是一种有效确保代码工作的方式。然而,如果不能全面地描述系统的行为,那么它们会带给你一种虚假的安全感)。事实上,我在看过这个概念后,发现在工作时,部门所用的thrift这种RPC,在写代码前先定义好主要的功能函数,包括输入输出,再进行开发,就是一个TDD&&BDD的过程。

2. 例子 : 以下是thrift的IDL(可以理解为是TDD,但是定义这个thrift通过了需求沟通,各部门协调,就可以算是BDD)。

//服务
service FavoriteSrv {
//功能
/**
* 是否已收藏
*/
IsFavorResp isFavor(1: IsFavorReq req) throws(1: SrvException e)
}
//返回值
struct IsFavorResp{
/**
* true表示已收藏, false表示未收藏
*/
1: bool isFavor
}
//输入参值
struct FavorReq{
1: ReqHeader header
/**
* 1: 收藏医院, 2: 收藏医生
*/
2: i32 favorType
/**
* 操作类型 0: 取消收藏, 1: 收藏
*/
3: i32 operType
4: i64 objId
5: i64 accountId
}


过度保护

同在《有效的单元测试》一书中,也是因为娄哥在测试语音识别测试时念了过分保护一章,引起了我的兴趣从而翻看。当天正好有一个代码要加上错误处理,而错误处理一直在工作中和其他同事有分歧,即某个地方是否需要加上错误处理。以下是个人体悟总结而不是书本摘抄,可能有误

1. 结论:在何时需要增加守卫语句(空值或错误检查)。

不加上无法或难以定位到错误发生的所在地

会引发业务异常而不发生错误

2.例子:go不完全代码(是否需要处理string转date的错误)

//将req.DateYM(string)类型转换为date类型
startYM, err := time.ParseInLocation(constants.DateFormatYYYYMM, req.DateYM, time.Local)
if err != nil {
return nil, err
}
//将endYM设置为本月的最后一天,通过加一个月同时减去一天
endYM := startYM.AddDate(0, 1, -1)


这个例子的错误处理是必须的,如果不处理:

可能得到一个错误的与业务不相符的错误数据,而不是引起程序异常结束。

可能在下面AddDate时才发生错误,而不是在Parse时,从而难以定位错误。

在go中,如果不处理err,并且不处理发生错误后的panic,会引起进程的异常关闭。(我遇到的情况已经recover接受了panic,如果以上两点不满足,是可以不处理err的,而是直接读取panic信息进行debug)

可以不处理的情况如下:

如果err的错误会直接引起程序异常结束(不会输出错误业务数据),那我们能够直接定位到错误(在go中就是panic后的信息),而不是难以定位(书作者中提到选择调试的难度和代码的简洁,他选择代码简洁)。

go的题外话:我们应该在错误发生时及时处理还有一个原则,不能引起程序panic,或者说panic后有专门的处理方法(推荐或者一般都会在函数中写上recover处理panic防止进程突然关闭,但是我们在init失败时应该在程序直接关闭而不是用recover阻止。具体前上篇文章。)

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: