FizzBuzz与写代码的“一万”个细节

摘要:

技术是由一万个细节组成的,哪怕一个这么简单的题目,也有如此多的点。我也不敢说自己是什么高手,起码写了许多年代码,也就把自己写代码的思维展示给大家,希望对有心人有所帮助。

非初学者向,虽然题是个简单的题,但要求读者有一定的敏捷工程实践及DDD相关经验。

FizzBuzz是一个经典的TDD入门题目,麻雀虽小,五脏……勉强算全吧。Stack Overflow创始人曾经在他的一本书里写到,“不要假设程序员都会写程序,招一个程序员来先写个FizzBuzz看看,结果可能会令你吃惊。”

我当时不信,于是在一个招聘活动上拿这个的一个完整版做了题目,结果也确实挺让我吃惊的,吃惊在哪我先卖个关子,后面详细说。后来教人写程序也用了这个题几百遍了,见识过各种各样奇怪的错误。

我们今天就用这个题目为例,来尽量阐述一些道理。这个题的需求有很多步,就好像软件开发中很多需求是一个版本一个版本迭代出来的,所以我们这个题目也一个迭代一个迭代来。

迭代一

迭代一的需求如下:

你是一名体育老师,在某次课距离下课还有五分钟时,你决定搞一个游戏。此时有200名学生在上课。游戏的规则是:

  1. 让所有学生拍成一队,然后按顺序报数。
  2. 学生报数时,如果所报数字是3的倍数,那么不能说该数字,而要说Fizz;如果所报数字是5的倍数,那么要说Buzz。

不同于凭本能思考,这里我们讲一个套路:我们做软件开发的时候可以刻意分三个问题域来考虑问题,我称之为业务域、方案域、实现域。这三个域有什么用呢?

当我们在进行软件开发的时候,有时会陷入无思路的状态,一旦陷入这种状态人容易焦虑,卡很久却没什么进展。这个时候我们往往是处于一种所谓的unknow unknown的状态。也就是不知道自己不知道什么。新人最容易陷入到这种状态,只好盯着屏幕看半天。这个时候就需要先意识到自己处于这个状态,然后就可以借用这三个域作为跳板跳出这个状态。

首先来看看你的问题到底在哪个域,在不同的域要采用不同的方法来探寻问题到底是什么,在这基础上就逐渐有了思路。这就是这三个域的用处。

具体怎么用呢?我们一个个来说。

业务域

首先说业务域,这里的业务用以代指需求。

以这个题为例,我们在读需求的时候会发现一个问题,被3整除返回Fizz,被5整除返回Buzz,被3和5整除返回什么?

这个问题很明显,就属于业务域的问题。

那么业务域的问题,我们通常怎么处理呢?

有的同学就直接脑补了:

  • 脑补一:能被3和5整除,那就是先被3整除呗,那就Fizz。
  • 脑补二:能被3和5整除,那就返回FizzBuzz呗。

那么以上哪个脑补是对的呢?

答案是以上都不对,脑补本身就不对,脑补只是猜测,猜测不经验证就是伪需求。当我们遇到业务域的问题,不要自己脑补,请去与需求方确认需求。

(题外话:当然,你带着两个脑补去找需求方是可以的,甚至于是很好的,因为这样需求方就能更容易的听懂你的问题,比你问被3和5整除返回什么要更具体。这个题目里被3和5整除是很清楚的,但在工作中,提一个抽象的问题,然后跟上两个可能的具体的解决方案也能帮助对方理解。)

确认需求时最重要的就是对概念的理解建立共识,识别概念的边界。前者还好,后者容易疏忽,同一个名词即便在需求当中,由于上下文的不同也有可能指的是两个概念,这部分内容本篇不作详细讨论。

经过业务域的确认,我们得到了一个完善后的需求。

你是一名体育老师,在某次课距离下课还有五分钟时,你决定搞一个游戏。此时有200名学生在上课。游戏的规则是:

  1. 让所有学生拍成一队,然后按顺序报数。
  2. 学生报数时,如果所报数字是3的倍数,那么不能说该数字,而要说Fizz;如果所报数字是5的倍数,那么要说Buzz。
  3. 学生报数时,如果所报数字同时是两个特殊数的倍数,也要特殊处理,比如3和5的倍数,那么不能说该数字,而是要说FizzBuzz。
方案域

方案域就是想想代码写完之后什么样,我们这里讲一个简单的,我称之为上下文图

上下文图表达的是代码的静态关系。比如,如果我的代码要这么写:

那图就是这么画的:

如果代码要这么写:

那么图就是这么画的:

整个这个画图的过程,是对程序的一个拆解,这个拆解的过程实际上也是伴随着设计的。这个图的主要目的就是画出一个一个只有数据依赖、没有过程依赖的小型的上下文。

什么叫过程依赖?如下图所示:

上面的代码每一段if的逻辑执行完都调用了一个continue(类似的还有break和return),这使得每一个if的block都是与外面的for block耦合的,我无法单独把其抽取成一个函数。也就没法单独测试,它的可测试性一点都不高。

如果我不把程序拆成只有数据依赖没有过程依赖的小型上下文,那么无论是从调试还是测试的角度都会变得很复杂,因而实现也会变得复杂。

这段程序另一个可测试性差的地方在于直接输出到了标准输出流里,如果标准库没有给我们提供打桩的方法,那么这个程序就只能人肉测。即便提供了,测试成本也提升了,远不如直接测一个字符串来的容易。所以我们在考虑输入输出的时候,也要考虑输入是否容易准备、输出是否容易获得。

比起画上下文读这个图,得到可测试性高的程序设计,这个状态才是我们想要的。当你有足够多的经验后&#x

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值