เรื่องนี้เกิดขึ้นในตอนเช้าวันอาทิตย์ ที่ผมกำลังฝึกใช้ FizzBuzz อยู่

ผมได้ยินมาว่า FizzBuzz จะถูกใช้ในการสัมภารณ์ด้วย ผมเลยว่าจะลองเล่นกับมันดูหน่อย

สามารถศึกษา FizzBuzz ได้ ที่นี้

วิดีโอที่ผมสร้างในส่วน TDD นั้นจะท้ายบทความครับ

อันดับ 1:

  • สร้างคลาสสำหรับการทดสอบ

  • คัดลอกกฎต่าง ๆ ตามช่องคอมเมนต์

  • รูปแบบของกฎทำให้มันเข้าใจง่าย

  • เพิ่มเติมตัวอย่างซักหน่อย ที่ทำให้เราเข้าใจได้

การทดสอบแรกของผม

การทดสอบแรกเขียนได้ดังนี้

@Test 
public void fizzBuzzConvertorLeavesNormalNumbersAlone(){ 
  FizzBuzzConverter fizzBuzz = new FizzBuzzConverter(); 
  Assert.assertEquals("1", fizzBuzz.convert(1)); 
}

ผมได้สร้างคลาส FizzBuzzConverter และวิธีการ convert

ผมเพิ่ม assertion ลำดับ 2 ในการทดสอบครั้งนี้ด้วย

Assert.assertEquals("2", fizzBuzz.convert(2));

ผมได้ทำการเปลี่ยนเป็นตามค่าดั้งเดิม

return String.valueOf(toConvertToFizzBuzz);

การทดสอบแรก

หลายคนอาจจะไม่ชอบ assertion หลายตัวในการทดสอบ

บางทีผมก็ทำ บางทีผมก็ไม่ทำ

นี้ไม่ใช่ความคิดผม

  • ชื่อที่ใช้ในการทดสอบอนุญาตให้ผมใช้ assertion หลายครั้ง
  • ผมคิดว่าวิธีการทดสอบหลายครั้งอาจจะทำให้มันยากขึ้น แทนที่จะใช้ Grok

การทดสอบครั้งที่สอง

ทดสองครั้งที่สองเป็น

@Test 
public void fizzBuzzConvertorMultiplesOfThree(){ 
  FizzBuzzConverter fizzBuzz = new FizzBuzzConverter(); 
  Assert.assertEquals("Fizz", fizzBuzz.convert(3)); 
}

นี่บังคับให้ผมต้องหาร 3 ให้ลงตัว

if(toConvertToFizzBuzz%3==0){ 
  return "Fizz"; 
}

ผมได้จินตนาการว่า  ถ้าคุณไม่รู้โมดูลทำงานอย่างไร  ทำให้ FizzBuzz สามารถเป็นไปค่อนข้างยาก

ผมได้เรียนรู้เบื้องหลังของโมดูลในวันนั้นแหละ  และลองทำโปรแกรม 8 บิท ขึ้นมา มีการทดลองเขียนหลายอย่าง clipping, และ scrolling routinesวนไปวนมาตั้งแต่นั้น

การทดสอบครั้งที่สาม

การทดสอบครั้งที่สามเป็น

@Test 
public void fizzBuzzConvertorMultiplesOfFive(){ 
  FizzBuzzConverter fizzBuzz = new FizzBuzzConverter();
  Assert.assertEquals("Buzz", fizzBuzz.convert(5)); 
}

ใช้เงื่อนไขเดียวกับตัวหาร 3 ลงตัว

if(toConvertToFizzBuzz%5==0){ 
  return "Buzz"; 
}

นี่เป็นวิธีการ convert ของผม สามารถดูได้เลยตามนี้

public String convert(int toConvertToFizzBuzz) { 
  if(toConvertToFizzBuzz%5==0){ 
    return "Buzz"; 
  } 
  if(toConvertToFizzBuzz%3==0){ 
    return "Fizz"; 
  } 
  return String.valueOf(toConvertToFizzBuzz); 
}

ผมได้เจอคนที่สร้างโค้ดที่ซับซ้อนสำหรับ FizzBuzz.

ผมยังคงยึดพื้นฐานที่แสนง่ายเหล่านั้นไว้อยู่ ถ้ามันทำงานได้ ผมก็แค่ refactor อย่างที่โปรแกรมเมอร์ดีๆควรจะทำ

การทดสอบครั้งที่ 4

การทดสอบครั้งที่สี่ก็ยังคงเหมือนกัน

@Test 
public void multiplesOfBothThreeAndFive(){ 
  FizzBuzzConverter fizzBuzz = new FizzBuzzConverter(); 
  Assert.assertEquals("FizzBuzz", fizzBuzz.convert(15)); 
}

ในจุดนี้ เรามาดูกันว่าผมคิดอย่างไรกับวิธีการแปลงนี้

  • ผมควรเพิ่ม flag เพื่อดู fizz และ buzz ดีไหม

  • ผมควรจะมี if else ซ้อนกันหลายชั้นดีไหม

  • บางทีผมใช้แค่ tertiary operator ก็พอ

ทันใดนั้น ผมตัดสินเขียนให้ง่าย

if(toConvertToFizzBuzz%15==0){ 
  return "FizzBuzz"; 
}

ความคิดของการทดสอบที่ 4

ผมสงสัยว่าจุดที่ทำให้คนใช้ FizzBuzz พลาดคือ โค้ดที่เขียนซับซ้อนเกินไป

หากดูวิธีการของผมไม่มีอะไรซับซ้อนเลย

public String convert(int toConvertToFizzBuzz) { 
  if(toConvertToFizzBuzz%15==0){ 
    return "FizzBuzz"; 
  } 
  if(toConvertToFizzBuzz%5==0){ 
    return "Buzz"; 
  } 
  if(toConvertToFizzBuzz%3==0){ 
    return "Fizz"; 
  } 
  return String.valueOf(toConvertToFizzBuzz); 
}

นี่เป็นเพียงการจัดลำดับว่าโค้ดไหนควรอยู่ตรงไหน

  • 15 สำคัญสุด เพราะว่ามันต้องทำการคูณ 3 กับ 5

  • 3 กับ 5 เท่ากับความสำคัญ และมันไม่ยังไม่รู้ว่ามันควรอยู่ตรงไหน

  • การเปลี่ยนค่าสตริงเป็นค่าเริ่มต้นดังนั้น จึงมีความสำคัญต่ำลงมา

วันวาน เราเคยถูกสอนว่าควร return แค่ค่าเดียวต่อ 1 method  ถ้าผมจะยืดแนวทางนี้ โค้ดผมจะยากเกินไปแทนที่ผมจะ

return

ตราบเท่าที่ตรงกับเงื่อนไข

ความจริงเป็นแล้ว เงื่อนไขเปรียบเสมือนตัวกรองไม่ให้ค่าที่เข้ามา คืนออกไปเป็นค่าเริ่มต้น

เสร็จแล้ว

ในที่สุดก็เสร็จสักที

แล้วในท้ายที่สุดผมมีอัลกอริทึมที่จะเปลี่ยนจำนวนเต็ม ในช่วง 1 ถึง 100 เป็น Fizz, Buzz, จำนวน หรือ FizzBuzz

ผมได้ใส่วงเล็บทั้งสองข้าง ด้วยบางอย่างที่ทำหน้าที่ แสดงผลค่านี้ออกไป

@Test 
public void outputTheHundredFizzBuzzes(){ 
  FizzBuzzConverter fizzBuzz = new FizzBuzzConverter(); 
  for(int i=1; i<=100; i++){ 
    System.out.println(fizzBuzz.convert(i)); 
  } 
}

ผมได้สร้าง @Test ขึ้นมา เพื่อความสะดวกต่อการรันโค้ดบน IDE

จากนั่นเทสเตอร์ (Tester) ก็เดินเข้ามา

  • ผมได้ใช้ TDD เพื่อออกแบบอัลกอริทีม

  • ผมไม่ได้ทดสอบค่า output เป็นกิจวัตร ผมได้ execute เพื่อดูผลลัพธ์จาก 1-100

  • ผมไม่ได้ยึดเกณฑ์อะไรมากมาย แค่ผมได้เห็นค่า output จาก outputTheHundredFizzBuzzes ตรงกับที่ผมต้องการ

ถ้านี้เป็นการสัมภาษณ์ ผมควรจะแปลง outputTheHundredFizzBuzzes เป็น main method แทน นี่เป็นการ implement แบบพื้นฐานให้ตรงตาม requirements ในฐานะเทสเตอร์ ผมไม่แน่ใจว่า ผมโน้มน้าวตัวเองว่า มันทำงานได้ หรือว่า มันทำงานได้จริงๆ คุณสามารถดูโค้ดทั้งหมดได้จากเรโปฯนี้

วีดีโอ