专栏首页小巫技术博客TDD练功房之FizzBuzz

TDD练功房之FizzBuzz

题目内容:

说明

  • 限时5分钟完成
  • 可以选用擅长的语言完成,例如 C、C++、Java、C#、Javascript、Python、Scala 等
  • 代码完成后必须附上 Readme 纯文本文档(推荐使用 markdown 排版)
  • 必须有自动化测试代码进行验证
  • Readme 文档中应描述如何运行单元测试或主程序来证明题目的正确性(至少针对测试用例输入能够得到对应输出)

题目内容 有一名体育老师,在某次离下课还有五分钟时,决定玩一个报数游戏。此时有100名学生在上课,游戏的规则如下:

  1. 老师先说出两个不同的特殊数(都是个位数),比如3, 5;让所有学生拍成一队,然后按顺序报数;
  2. 学生报数时,如果所报数字是「第一个特殊数(3)」的倍数,或者包含「第一个特殊数(3)」,那么不能说该数字,而要说Fizz;
  3. 学生报数时,如果所报数字是「第二个特殊数(5)」的倍数,或者包含「第二个特殊数(5)」,那么不能说该数字,而要说Buzz;
  4. 如果所报数字同时是「两个特殊数」的倍数,也要特殊处理。例如,如果是「第一个(3)」和「第二个(5)」特殊数的倍数,那么也不能说该数字,而是要说FizzBuzz
  5. 学生报数时,如果所报数字包含了「特殊数」,那么也不能说该数字,而是要说对应的英文单词(见规则1和规则2)。例如,要报13的同学应该说Fizz;要报52的同学应该说Buzz。
  6. 如果在一次报数中,匹配上述多个规则,Fizz和Buzz都只能出现一次。
  7. 否则,直接说出要报的数字。

在开始做题之前,先问一下自己几个问题:

  • 什么是单元测试?
  • 为什么要写单元测试?
  • 怎么写单元测试?

这里我就不鼓吹单元测试有多么好,能带来什么好处。一切都以结果为导向,笔者也是本着学习的心态,跟着大神们去领略代码之美,让自己也能写出健壮的代码,告别996(OS:虽然我现在也没有996)。

先花5~10分钟理解题目,基本能知道这是个报数游戏,我们输入一个数字,让它输出是结果有4种:

  • Fizz
  • Buzz
  • FizzBuzz
  • 数字本身

如果对代码没有要求的话,我们最开始可能会写很多if else的代码,例如下面代码:

 public class FizzBuzz {

    public static void sayNumberGame(int firstNum, int secondNum) {

        // 从1~100报数

        for (int i = 1; i <= 100; i++) {

            // 即是第一个特殊数字的倍数又是第二个数字的倍数说FizzBuzz

            if (isMultipleNum(firstNum, i) && isMultipleNum(secondNum, i)) {

                System.out.println(String.format("%s Don't Say number, but say FizzBuzz", i));

                continue;

            }



            // 第一个特殊数字的倍数说Fizz

            if (isMultipleNum(firstNum, i)) {

                System.out.println(String.format("%d is a multiple of %d or contains %d Say Fizz", i, firstNum, firstNum));

                continue;

            }

            // 第二个特殊数字的倍数说Buzz
            if (isMultipleNum(secondNum, i)) {

                System.out.println(String.format("%d is a multiple of %d or contains %d Say Buzz", i, secondNum, secondNum));

                continue;

            }
            // 不满足以上所有条件
            System.out.println(String.format("%s Say number", i));

        }

    }



    public static boolean isMultipleNum(int targetNum, int sayNum) {

        String targetNumStr = String.valueOf(targetNum);

        String sayNumStr = String.valueOf(sayNum);

        return sayNum % targetNum == 0 || sayNumStr.contains(targetNumStr);

    }



    public static void main(String[] args) {

        System.out.println("00.FizzBuzz!!!");

        sayNumberGame(3, 5);

    }

}

上面代码有什么问题?

  • 太多if else
  • 重复代码太多
  • 没有单元测试,通过人肉测试
  • 没有自动化测试
  • 通过println打印内容
  • 方法超出5行
  • 没有端到端测试 这个是我最开始无脑贴代码被老师怼的点评。后面想了想确实有道理,明明代码可以写得更好,作为一个有追求的程序员必须得改。

这次我先写测试,将不同情况的输出分别写了测试方法来验证:

public class FizzBuzzTest {
    @Test
    public void testSayFizz() {
        assertEquals("Fizz", FizzBuzz.fizzBuzz(3, 3, 5));
    }

    @Test
    public void testSayBuzz() {
        assertEquals("Buzz", FizzBuzz.fizzBuzz(5, 3, 5));
    }



    @Test
    public void testSayFizzBuzz() {

        assertEquals("FizzBuzz", FizzBuzz.fizzBuzz(15, 3, 5));

    }



    @Test
    public void testOnlySayNum() {

        assertEquals("1", FizzBuzz.fizzBuzz(1, 3, 5));

    }
}

注:这里我用的IDE是Intellij,测试框架用的是Junit4.

代码我也进行了重构:

public class FizzBuzz {
    private static final String FIZZBUZZ = "FizzBuzz";
    private static final String FIZZ = "Fizz";
    private static final String BUZZ = "Buzz";

    public static String fizzBuzz(int sayNum, int firstNum, int secondNum) {

        // 即是第一个特殊数字的倍数又是第二个数字的倍数说FizzBuzz
        if (isFizzBuzz(sayNum, firstNum, secondNum)) {
            return FIZZBUZZ;
        }

        // 第一个特殊数字的倍数说Fizz
        if (isFizz(sayNum, firstNum)) {
            return FIZZ;
        }

        // 第二个特殊数字的倍数说Buzz
        if (isBuzz(sayNum, secondNum)) {
            return BUZZ;

        }

        // 不满足以上所有条件
        return String.valueOf(sayNum);
    }



    public static boolean isFizzBuzz(int sayNum, int firstNum, int secondNum) {
        return isFizz(sayNum, firstNum) && isBuzz(sayNum, secondNum);

    }

    public static boolean isFizz(int sayNum, int targetNum) {

        return isMultipleOrContainNum(sayNum, targetNum);

    }

    public static boolean isBuzz(int sayNum, int targetNum) {

        return isMultipleOrContainNum(sayNum, targetNum);

    }

    public static boolean isMultipleOrContainNum(int sayNum, int targetNum) {
        return (sayNum % targetNum == 0 || formatNumToString(sayNum).contains(formatNumToString(targetNum)));

    }

    public static String formatNumToString(int num) {
        return String.valueOf(num);
    }
}

可以看到重构后的代码已经差别很大,但可测性明显提升了不少,基本可以从方法名表达意图,可读性也提升了。

最后自然是全绿通过:

本文分享自微信公众号 - 巫山老妖(wwjblog),作者:污748

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

原始发表时间:2019-05-19

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

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 测试驱动开发 Test-Driven Development

    本篇文章阅读时间:10min 读者预期的收获是: 认识测试驱动开发 非常简单开启你的TDD之旅 可以编写自动化测试 重构、重新设计旧的代码更加自信 引子 (...

    QQ音乐前端团队
  • TDD案例-FizzBuzz

    FizzBuzz是一个非常适合各种场景使用的编程题目,也被各路大佬推荐用于TDD的教学。具体的FizzBuzz题目描述可参见 http://codingdojo...

    Antony
  • TDD案例-FizzBuzz-重构

    在之前的的TDD案例-FizzBuzz文章中,我们介绍了如何以TDD的方式,通过5个测试用例,来驱动我们实现了FizzBuzz。 本文将继续对FizzBuzz的...

    Antony
  • 在家隔离,不忘学习-FizzBuzz-TDD

    在之前的的TDD案例-FizzBuzz文章中,我们介绍了如何以TDD的方式,通过5个测试用例,来驱动我们实现了FizzBuzz。 本文将继续对FizzBuzz的...

    Antony
  • 如何使用Python进行单元测试

    在我的日常工作中,我是一名专业程序员。我使用c++、c#和Javascript。我是一个开发团队的一员,他们使用单元测试来验证我们的代码是否按照它应该的方式工作...

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

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

    ThoughtWorks
  • PyTorch训练神经网络玩游戏

    从1开始数数,当遇到3的倍数的时候,说fizz,当遇到5的倍数的时候,说buzz,当遇到15的倍数的时候,就说fizzbuzz,其他情况则正常数数

    mathor
  • 开发 | MxNet李沐:机器学习简介——动手学深度学习0.1

    AI科技评论注:本文作者为深度学习平台MxNet的作者李沐,文章由AI科技评论整理自作者的机器学习网站“动手学深度学习”。在这个网站中,李沐介绍了他做这个项目的...

    AI科技评论
  • 用Python进行单元测试

    Python编程语言,不仅仅在机器学习、数据分析等领域大放异彩,在web开发中等软件开发中,使用者也越来越多。

    老齐
  • 让我们再聊聊TDD 续——人人都在做TDD|洞见

    在上一篇文章里面,通过对DHH的文章以及DHH和Kent Beck等讨论的分析,我阐述了对TDD的理解和分类,现在来继续聊聊TDD的实施和分层。 现在还有非常多...

    ThoughtWorks
  • 也谈“精益”|洞见

    精益对大家来说都不陌生了,无论是最开始提取的丰田制造原型,还是后面延伸出来的物流供应链管理,再到近两年颇为流行的精益创业(Lean Startup),都在不停刷...

    ThoughtWorks
  • TDD测试驱动开发的实践心得

    笔者对此深以为然,但这并不是信口雌黃的结论,也不是因为谁说了就认定他是对的,这是基于笔者自己在TDD上的一些实践的经验得出来的结论。而且笔者关于TDD的一些细节...

    御剑
  • 简单设计落地三板斧

    如果你认同 简单设计的价值观,我相信 解析简单设计原则 对你来说很容易理解并接受,它不像面向对象设计原则(比如:SOLID)那么晦涩难懂,它给你指明了一条明朗...

    袁慎建@ThoughtWorks
  • 测试驱动进行开发

    测试驱动开发,英文全称Test-Driven Development,简称TDD,是一种不同于传统软件开发流程的新型的开发方法。它要求在编写某个功能的代码之前先...

    用户1348170
  • 一行Python代码竟可以实现这些可赞的功能

    Python一直倡导Beautiful、Explicit、Simple等原则,接下来要介绍的一行Python能实现哪些好玩的功能,可能和Explicit原则相违...

    1480
  • 几个小例子告诉你, 一行Python代码的奇淫技术

    首先你要了解一下Python之禅,一行代码输出“The Zen of Python”:

    一墨编程学习
  • 1 行Python代码能干哪些事,这 13个你知道吗?

    来源:http://www.techug.com/post/what-can-a-line-of-python-code-do.html

    昱良
  • 1 行 Python 代码能干哪些事,这 13 个你知道吗?

    Python 之禅有一句话叫 “Simple is better than complex.”,简单,到底能多简单,一行代码?

    崔庆才
  • 除了AutoGraph还有 ...... 这两天TensorFlow真是会搞事情

    【人工智能头条导读】昨天谷歌发布了 TensorFlow 的一个新工具 —— AutoGraph,可以将 Python 代码快速转化到 TensorFlow 的...

    用户1737318

扫码关注云+社区

领取腾讯云代金券