前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >TDD练功房之FizzBuzz

TDD练功房之FizzBuzz

作者头像
巫山老妖
发布2019-05-31 17:35:28
2.3K0
发布2019-05-31 17:35:28
举报
文章被收录于专栏:小巫技术博客小巫技术博客

题目内容:

说明

  • 限时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的代码,例如下面代码:

代码语言:javascript
复制
 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行
  • 没有端到端测试 这个是我最开始无脑贴代码被老师怼的点评。后面想了想确实有道理,明明代码可以写得更好,作为一个有追求的程序员必须得改。

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

代码语言:javascript
复制
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.

代码我也进行了重构:

代码语言:javascript
复制
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);
    }
}

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

最后自然是全绿通过:

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-05-19,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 巫山老妖 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档