what-is-dependency-injection-980x735
จากปัญหาเรื่อง Kata FizzBuzz  นั้นเป็นปัญหาที่ง่ายมาก ๆ
ดังนั้นเราจึงสามารถแก้ไขปัญหาได้หลากหลายแนวทาง
ซึ่งมันเหมาะสำหรับการเรียนรู้
ฝึกฝนกระบวนการคิด
ฝึกฝนการเขียนโปรแกรมตามแนวคิด Test-Driven Developer (TDD)

ดังนั้นมาดูอีกแนวทางในการแก้ไขปัญหา คือ Dependency Injection
เริ่มกันเลย

ถ้าเขียนแบบง่าย ๆ จะเป็นดังนี้

ผมเรียกว่า Cowboy FizzBuzz ก็แล้วกัน

 

แต่เราลองมาเปลี่ยนแนวคิดหน่อยไหม ?
เราลองมาฝึกกันหน่อย ว่ายังมีวิธีการอื่น ๆ อีกไหม ?
ซึ่งผมลองใช้แนวคิดของ Dependency Injection กับ Composition มาแก้ไขปัญหา

ภาพรวมของแนวคิดการแก้ไขปัญหา

จาก code ของ Cowboy FizzBuzz นั้น
สามารถแบ่งหน้าที่การทำงานออกเป็น 3 ส่วนหลัก คือ

  1. Provider สำหรับเตรียมชุดของตัวเลข ตั้งแต่ 1 ถึง 100
  2. Printer สำหรับการแสดงผลออกที่ System.out หรือ Standard output
  3. Message/Text Provider สำหรับการประมวลผลว่าตัวเลขจะได้ค่าอะไรออกมา เช่น Fizz, Buzz และ FizzBuzz เป็นต้น

แสดงดังรูป

fizzbuzz-di
เป้าหมายของแนวคิดนี้ ประกอบไปด้วย

  • แยกหน้าที่การทำงานกันอย่างชัดเจน
  • ส่วนการทำงานต่าง ๆ เป็นอิสระต่อกัน
  • สามาถทำการทดสอบแต่ละส่วนได้ง่าย

ปล. ก่อนที่จะอ่านต่อไป แนะนำให้ลองแก้ไขปัญหาด้วยแนวทางนี้เองก่อนนะ

เพื่อความเข้าใจมากขึ้น มาดูขั้นตอนการพัฒนาดีกว่า

1. สร้าง class NumberProvider

เริ่มจากสร้างชุดการทดสอบง่าย ๆ
เพื่อทำการสร้างชุดตัวเลขตั้งแต่ 1 ถึง 100

ปล. เราสามารถใช้ Test double ได้นะ แต่จากตัวอย่างไม่ได้ใช้

จะได้ code ใน class NumberProvider ดังนี้

2. สร้าง Printer สำหรับแสดงผลการทำงานผ่าน System.out

เริ่มจากสร้างชุดการทดสอบ
ซึ่งมีความน่าสนใจอย่างมากดังนี้

จะได้ code ใน class Printer ดังนี้

3. สร้าง TextProvider

เป็นส่วนการประมวลผลตามเงื่อนไขต่าง ๆ ของ FizzBuzz นั่นเอง
โดยเริ่มจากชุดการทดสอบ 4 กลุ่ม ดังนี้

จะได้ code ง่าย ๆ ใน class MessageProvider
ซึ่งจากวิธีการนี้ยังไม่ได้เน้นในส่วนนี้ จึงทำงานในลักษณะเดิม ดังนี้

4. นำทั้ง 3 ส่วนมารวมกันด้วยแนวคิด Dependency Injection

เริ่มด้วยการเขียนชุดการทดสอบเช่นเดิม
โดยการเขียนจะใช้วิธีการที่เรียกว่า Test Double มาช่วย
พร้อมกับใช้ Mockito ซึ่งเป็น mocking framework มาช่วยนิดหน่อย

โดยชุดการทดสอบประกอบไปด้วย 3 ส่วน เพื่อสร้างการทำงานใน class MyFizzBuzz ขึ้นมา ดังนี้

4.1 เริ่มจากการสร้าง NumberProvider ขึ้นมา
เพื่อตรวจสอบว่า
method getNumbers() ใน class NumberProvider ถูกเรียกใช้งานนะ ดังนี้

ซึ่งทำให้เกิด code ใน class MyFizzBuzz ดังนี้

4.2 ทำการเพิ่ม TextProvider เข้ามา เพื่อประมวลผลการทำงานตามกฏของ FizzBuzz
การทดสอบจะมีขั้นตอนการเตรียมข้อมูลเยอะขึ้นมานิดหน่อย
รวมทั้งใช้ Mockito มาเพื่อทำการ verify การทำงานว่าถูกต้อง
และทำงานตามลำดับหรือไม่ ?
ดังนี้

คำอธิบายของชุดการทดสอบ

  • NumberProvider นั้นจะสร้างข้อมูลตั้งแต่ 1 ถึง 3
  • ใช้ InOrder จาก Mockito เพื่อตรวจสอบการเรียกใช้งาน method getText() ของ class TextProvider ซึ่งจะต้องเรียงใช้งาน 1, 2 ,3 ตามลำดับเสมอ

ซึ่งทำให้เกิด code ใน class MyFizzBuzz ดังนี้

4.3 ทำการเพิ่ม Printer เข้าไป เพื่อแสดงผลลัพธ์จากการทำงาน
เป็นชุดการทดสอบที่ต้องเตรียมข้อมูลเริ่มต้นเยอะหน่อย
เนื่องจากมีส่วนการทำงาน หรือ dependency ที่เยอะขึ้น ประกอบไปด้วย
กำหนด NumberProvider นั้นจะสร้างข้อมูลตั้งแต่ 1 ถึง 3
กำหนด TextProvider ให้แสดงข้อมูลตามที่เราต้องการ จากตัวอย่าง ผมให้ส่งค่าที่มีคำว่า xx อยู่ข้างหน้าข้อมูล
สุดท้ายทำการตรวจสอบว่า method print() ใน class Printer ถูกเรียกใช้งานตามลำดับถูกต้องหรือไม่

ดังนี้

ซึ่งทำให้เกิด code ใน class MyFizzBuzz ดังนี้

คำอธิบาย
Code ที่เกิดใน class MyFizzBuzz นั้นมันมี 2 ส่วนที่น่าสนใจ คือ

  • มี setter method สำหรับการ inject ส่วนการทำงานต่าง ๆ ที่จำเป็นเข้ามาได้
  • ในส่วนการทำงานหลัก จะทำการรวม หรือ composition ส่วนการทำงานต่าง ๆ เข้าด้วยกัน ซึ่งมันง่ายต่อการทำความเข้าใจ

สุดท้ายแล้ว

น่าจะพอทำให้เห็นแนวคิด และ แนวทางการแก้ไขปัญหาอีกรูปแบบหนึ่ง
เป้าหมายหลัก ๆ เพื่อให้แต่ละส่วนการทำงานแยกออกจากกัน
ซึ่งส่งผลให้สามารถทดสอบได้ง่าย

มาหาวิธีการแก้ไขปัญหา FizzBuzz แบบอื่น ๆ กันครับ

ตัวอย่าง code ทั้งหมดอยู่ที่ GiutHub:Up1:Kata fizzbuzz with dependency injection