Screen Shot 2558-04-28 at 1.31.16 PM
ช่วงนี้มีโอกาสพาทีมพัฒนา ฝึกเขียน code กันพอสมควร
ซึ่งมักจะเขียน code แบบ paring กัน
และให้แก้ไขปัญหาต่างๆ จาก Cyber Dojo
โดยหนึ่งในปัญหาที่ใช้ฝึกกันก็คือ Print Diamond 
มาดูกันว่าแก้ไขกันอย่างไรบ้าง ?

ปัญหา Diamond Print

ทำการรับ input เป็นตัวอักษรเข้ามา
แล้วผลที่ออกไปคือการแสดงผลเป็นรูปเพชร
มีตัวอย่างดังนี้

ขั้นตอนการแก้ไขปัญหา

วิธีการแก้ไขปัญหามีเยอะแยะมากๆ
โดยที่กฎกติกามารยาทในการฝึกฝน กำหนดไว้ว่า

  1. จะต้องเขียน test ที่ fail ก่อน
  2. จากนั้นเขียน code เพื่อให้ test ผ่าน
  3. ทำการ refactor code ที่ไม่ดี ให้ดีขึ้น
  4. จากนั้นกลับไปเขียน test ที่ fail ต่อไป ทำแบบนี้ไปเรื่อยๆ จนพอใจ

หนึ่งในวิธีแก้ไขปัญหาที่ผมลองใช้งานก็คือ Recycle test
เป็นวิธีการที่สอนให้เรารู้จัก
แบ่งปัญหาใหญ่ๆ ออกเป็นปัญหาย่อยๆ
เพื่อให้การแก้ไขง่ายขึ้น และ รวดเร็วขึ้น
อธิบายแบบนี้ไม่เห็นภาพ
ดังนั้นมาเริ่มเขียน code กันดีกว่า

Test case ที่ 1

เริ่มจาก test case ง่ายสุดคือ
Input เป็น A
Output เป็น A

พยายามเขียน code ให้ง่ายที่สุด
และแก้ไขด้วยวิธีการที่ง่ายที่สุด ดังนี้

Test case ที่ 2

Input เป็น B
Output เป็น
A
B B
A
โดย test case ถือว่าเป็นจุดที่คุณควรจะหยุดคิดหน่อยว่า !!
ปัญหาใน test case นี้มีอะไรบ้าง
ไม่เช่นนั้น คุณก็จะ hard code มันไปเลย
หลายๆ คนมักจะทำกันเพื่อให้ test ผ่าน
ซึ่งมันไม่ผิด แต่ไม่ถูกต้องมากนัก

ดังนั้นลองคิดหน่อยสิว่า ปัญหาใน test case ที่ 2 มีอะไรบ้าง ?

  1. การแสดงตัวอักษร คือ ABBA
  2. การขึ้นบรรทัดใหม่
  3. ช่องว่างหน้า A
  4. ช่องว่างระหว่าง B

ลองถามตัวเองก่อนว่า
เราจะแก้ไขปัญหาทั้ง 4 ปัญหาพร้อมๆ กันเลยหรือ ?
สำหรับผม ขอแก้ไขทีละปัญาดีกว่านะ
มันน่าจะแก้ไขได้ง่ายกว่า !!

ดังนั้นจึงเริ่มด้วย test case ง่ายๆ คือ

Input เป็น B
Output เป็น AB
บางคนอาจจะบอกว่าทำไมไม่เป็น ABBA ไปเลยล่ะ
ตรงนี้อยู่ที่ตัวเราเองครับ ว่าจะแก้ไขปัญหาด้วยแนวทางใด !!

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

คำถาม
จาก code นั้นเราเห็นอะไรแปลกๆ ไหม ?
หรือคิดว่า code มันดีแล้วหรือยัง ?
คำตอบ
เห็น duplication ไหม ?
นั่นคือการแสดงค่า A ไงล่ะ
และมันก็ตามด้วย B …
นั่นหมายความว่ามันแสดงผลเริ่มจาก A จนไปถึง input ที่ต้องการ
นั่นคือ
A => A
B => AB

ดังนั้นเมื่อเห็นรูปแบบของการทำงานแล้ว
เราสามารถทำการ refactor code ได้ดังนี้

เขียน test ต่อไปกันเลย

ซึ่งแน่นอนว่า case ก่อนหน้ามันพังแน่นอน
ดังนั้นจึงเปลี่ยนชื่อ test case ใหม่ซะเลย
ใน test case นี้ทำการแก้ไขปัญหา การแสดง B ซ้ำๆ
Input เป็น B
Output เป็น ABB

การแก้ไขก็ง่ายมากๆ เพียงต่อท้าย B เข้าไปดังนี้

ปล. ใน code ทำการ hard code ไว้นะ !!

ทำการเพิ่ม test case ใหม่เข้าไป

ซึ่งก็แก้ไขชื่อ test case เดิมซะ
โดยจะทำการแสดงตัวอักษรทั้งหมดที่เราต้องการออกมาเลย ดังนี้
Input เป็น B
Output เป็น ABBA

ปล. ใน code ทำการ hard code ไว้นะ !!

ต่อไปเราจะทำการเอา hard code นี้ออกไป

ด้วยการเพิ่ม test case ใหม่คือ
แต่ละบรรทัดต้องมี \n หรือ new line นั่นแหละ
จาก code จะเห็นได้ว่า
ผมแก้ไขด้วยการ hard code อีกแล้ว !!
แต่มันก็เพียงพอทำให้ test case นี้ผ่านนะ
จากนั้น เรามาทำการ refactor code ดีกว่า
ถ้าปล่อยไปนานกว่านี้ มันจะ refactor ยากมากๆ เลยนะ
ดังนั้น

ห้ามพูดคำว่า เดี๋ยวก่อน เดี๋ยวกลับมา refactor
ซึ่งเชื่อเถอะว่า ไม่เคยกลับมาทำหรอกนะ

เริ่มจากการทำให้ test case ผ่านก่อน ซึ่งก็คือการ hard code
จากนั้น ให้ทำการ refactor code กันเถอะ

โดยผมแยกการทำงานเป็น 3 ส่วนคือ

  1. ส่วนการแสดงบรรทัดที่ 1 คือ A\n
  2. ส่วนการแสดงบรรทัดที่ 2 คือ BB\n
  3. ส่วนการแสดงบรรทัดที่ 3 คือ A\n

เห็นไหมว่าบรรทัดที่ 1 กับ 3 มันเหมือนกันเลย
การแยกส่วนการทำงาน
ทำให้เราแก้ไขปัญหาได้ง่ายขึ้น !!

ปล. อย่าลืม case แรกด้วยนะครับ

จากนั้นเริ่มทำการ refactor code กันบ้าง !!

ขั้นแรก แยกการสร้างข้อมูลในแต่ละบรรทัดก่อน
ซึ่งผมแยกออกมาเป็น method ชื่อว่า buildLine() ดังนี้

code เริ่มเป็นสัดเป็นส่วนมากขึ้น !!

ต่อจากนั้นเข้าสู่ test case จริงๆ ที่เราต้องการแล้ว

นั่นก็คือใส่ช่องว่างเข้าไป
ซึ่งมีทั้งช่องว่างข้างหน้า และ ตรงกลางนะสิ
ดังนั้น ผมเลือกแก้ไขทีละปัญหาก็แล้วกัน
เริ่มที่ใส่ช่องว่างข้างหน้าก่อน

Input เป็น B
Output เป็น A\nB B\n A

เมื่อเขียน test case แล้ว ก็ทำการแก้ไขปัญหา
ผ่าน method buildLine() กันเลย

เมื่อทุกอย่างเรียบร้อย
คำถาม
ใน code คุณเห็นอะไรไหม ?
เห็น duplication อะไรไหม ?
คำตอบ
เห็นการแสดงช่องว่างไหม ?
มันทำงานซ้ำๆ นะ
ดังนั้นจึงทำการ extract ออกไปยัง method ชื่อว่า addSpace() ซะเลย
เพื่อให้ทำหน้าที่เฉพาะการสร้างช่องว่าง

คำถามก็คือ
method addSpace() จะต้องส่งค่าอะไรเข้าไปล่ะ ?
คำตอบ
ก็จำนวนของช่องว่างไงล่ะ ?

คำถาม
แล้วจะส่งค่าอะไรเข้าไปล่ะ 1, 2, 3, 4 ?
คำตอบ
ลองมานั่งคิดหน่อยไหมสัก 5 วินาที …..
เห็นอะไรไหม
A => ไม่มีช่องว่างหน้า A
B => มีช่องว่าง 1 ช่องว่างหน้า A
B => ไม่มีช่องว่างหน้า B

เราก็พอหาสมการแบบโง่ๆ ได้ใช่ไหมคือ
จำนวนช่องว่างที่จะแสดงข้างหน้า = ตัวอักษรที่ส่งเข้ามา (input) – ตัวอักษรปัจจุบัน
เช่น
A – A = 0
B – A = 1

ดังนั้นสามารถเขียน code ได้ดังนี้

คำถาม
เรายังเหลือปัญหาไหนอีกนะ ?
คำตอบ
ก็ช่องว่างระหว่างตัวอักษรไงล่ะ
ทำอย่างไรดีล่ะ ?

Test case ที่ 3 ทำการเพิ่ม test case ใหม่ไงล่ะ นั่นก็คือ C

จากนั้นจึงทำการแก้ไขปัญหา
ด้วยการใช้สิ่งที่เรามีอยู่คือ method addSpace() ไงล่ะ
คำถาม
จะใส่จำนวนเท่าไรเข้าไปดีล่ะ ?
ลองคิดกันหน่อยสิ !!
คำตอบ
ลองคิดกันหน่อยสิว่า จำนวนช่องว่างระหว่างตัวอักษรเป็นอย่างไรบ้าง ?
A = 0
B = 1
C = 3

จากลำดับอนุกรมแบบนี้คือ 0, 1, 3 มันมาจากสมการอะไรนะ
(A – A) * 2 -1 = 0
(B – A) * 2 -1 = 1
(C – A) * 2 -1 = 3

เมื่อเราได้สมการแล้ว ก็เขียน code เพื่อแก้ไขปัญหาสิครับ จะชักช้าอยู่ไปทำไม !!

มาถึงตรงนี้เราก็สามารถแก้ไขปัญหาได้ทั้ง 3 กรณี คือ A, B และ C
สิ่งที่ต้องทำต่อไปก็คือ

  • การ refactor code ให้โครงสร้างดีขึ้น
  • การเพิ่ม test case อื่นๆ เพื่อสร้างความมั่นใจให้กับเรา

สามารถดูขั้นตอนการแก้ไขได้ที่ Cyber Dojo :: Diamond Print

สุดท้ายแล้ว

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

ดังนั้น พยายามทำความเข้าใจปัญหา
แล้วแยกปัญหาออกมาเป้นปัญหาย่อยๆ
แล้วให้ทำการแก้ไขไปทีละปัญหา
จะทำให้เรามีกำลังใจในการแก้ไขปัญหา
รวมทั้งไม่จมอยู่กับปัญหาใหญ่ๆ

จากการทำ Cyber Dojo เป็นกลุ่ม ด้วยปัญหาเดียวกันนี้
พบว่าแต่ละคน และ แต่ละคู่ มักจะแก้ไขหลายปัญหาพร้อมกัน
ทำให้ code ที่ได้มาดูยุ่งเหยิง
รวมทั้งใช้เวลาในการแก้ไขแต่ละ test case นานมากๆ

ดังนั้นลองฝึกกันดูครับ
ผมเชื่อว่า ปัญหาหนึ่งๆ มันมีวิธีการแก้ไขปัญหาได้หลายวิธี
อยู่ที่ว่า เราจะควบคุมขั้นตอนการแก้ไขปัญหาได้หรือไม่เท่านั้นเอง

 

Reference Websites
Break down problem