จากการ review code ของทีมพัฒนาอีกครั้ง
พบว่ามี code ที่ซ้ำซ้อน (Duplication) อยู่ชุดหนึ่ง
ซึ่งอาจจะเกิดซ้ำแบบนี้ในอนาคตอันใกล้อีก
ดังนั้น มาดูกันว่าจะลด code ที่ซ้ำซ้อนเหล่านี้ลงได้อย่างไร

เริ่มต้นจากการทำความเข้าใจต่อผลกระทบที่เกิดจาก code ที่ซ้ำซ้อน

code ที่ซ้ำซ้อนเหล่านี้ มีสาเหตุหลักๆ มาจาก

  • การ copy-and-paste code เพราะว่าเมื่อนำ code มาใช้แล้วมันสามารถทำงานได้ตามต้องการ
  • การทำงานมีความคล้ายคลึงกันมาก ทำให้ code มีการทำงานที่เหมือนกัน โดยไม่ได้ตั้งใจ
  • การขโมยหรือนำ code มาใช้โดยไม่ได้รับอนุญาต

จากสาเหตุเหล่านี้จึงทำให้เกิดปัญหาตามมา ได้แก่

  • ค่าใช้จ่ายในการดูแลรักษา code ที่สูงขึ้น ตัวอย่างเช่นถ้ามีการแก้ไข จะต้องตามไปแก้ไขทุกๆ จุดที่ซ้ำซ้อนกัน
  • บ่งบอกได้ว่า การออกแบบจะต้องมั่ว และ ยุ่งเหยิง ต่อไปอย่างแน่นอน

มาดู code ที่ซ้ำซ้อนกันดีกว่า

ในส่วนของ code ที่ซ้ำซ้อนนั้น มีขั้นตอนการทำงานดังนี้

  1. ทำการอ่านข้อมูลจากไฟล์ CSV ( Comma Separate Value )
  2. ทำการอ่านข้อมูลจากไฟล์มาครั้งละ 1 บรรทัด แล้วทำการแยกข้อมูลออกมาด้วยตัวแบ่งที่กำหนดไว้ นั่นคือ comma
  3. ทำการแปลงข้อมูลที่แยกออกมา ไปอยู่ในรูปแบบของ object ที่ต้องการ ซึ่งเรียกว่าการ Unmarshall เช่นไปอยู่ใน object ของ Product และ Customer เป็นต้น
  4. ทำการเพิ่มข้อมูลลงใน List
  5. ทำการส่งค่าใน List กลับไปยังผู้เรียกใช้งาน

จากทั้ง 5 ขั้นตอน จะมีเพียงขั้นตอนที่ 3 เท่านั้นที่แตกต่างกัน

ตัวอย่าง code ที่ซ้ำซ้อนกัน ซึ่งมาจาก 2 class

  • CustomerCSVReader ทำการอ่านข้อมูลลูกค้า
  • ProductCSVReader ทำการอ่านข้อมูลสินค้า

ปล. อย่าลืมเขียน unit test ด้วยนะครับ ขาดไปไม่ได้เลย ผลการทำงานเป็นดังรูป

Screen Shot 2557-08-11 at 11.25.57 AM

ตอนนี้เราเห็นแล้วว่า code ที่ซ้ำซ้อนคืออะไร

ต่อไปลอง refactor code แบบง่ายๆ ก่อน

คือการ extract ส่วนที่แตกต่างออกมาเป็น method
โดยจะตั้งชื่อ method ว่า unmarshall ดังนี้

จากนั้นหยุดและคิดกันหน่อย

เราพบว่ามี code ที่ทำงานซ้ำซ้อนกัน ยกเว้นขั้นตอนที่สาม
ถึงแม้ว่าเราจะแยกเป็น method ออกมาแล้ว
มันกลับทำให้เห็นส่วน code ที่ซ้ำซ้อนมากยิ่งขึ้น

ดังนั้น วิธีการที่เราสามารถทำได้ เพื่อลดความซ้ำซ้อน
ด้วยการแยก การทำงานที่ซ้ำซ้อน ไปไว้ยังไฟล์ หรือ class เดียว
โดยที่ไม่จำเป็นต้องรู้เลยว่า ภายในมีการทำงานจริงๆ อย่างไร
นั่นคือการซ่อนรายละเอียดของการทำงานนั่นเอง
แล้วทำการเรียกใช้มันเลย น่าจะเป็นแนวทางที่ดีกว่าเดิม …

ประเด็น คือ เราจะทำอย่างไรดีล่ะ .. คิดก่อนทำ

เริ่มด้วยออกแบบด้วย class diagram ก่อน (ต้องคิด ก่อน ทำ เสมอ) ดังรูป

Screen Shot 2557-08-11 at 11.00.09 AM

โดยผมจะใช้แนวคิด Inheritance ด้วยรูปแบบ Template method มาช่วยลดความซ้ำซ้อน

ขั้นตอนการพัฒนา เป็นดังนี้

1. สร้าง Abstract class ขึ้นมาชื่อว่า AbstractCSVReader.java
เพื่อให้ทำการอ่านข้อมูลจากไฟล์ CSV
ทำการแยกข้อมูล และ unmarshall ข้อมูลลง object ที่ต้องการ
หลังจากนั้นก็ส่งค่ากลับไปยังผู้เรียกใช้

สิ่งที่น่าสนใจ ก็คือ
มี abstract method ชื่อว่า unmarshall
เนื่องจากเป็นขั้นตอนการทำงาน ที่แตกต่างกันตามชนิดของข้อมูล นั่นคือ Customer และ Product

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

2. ส่วน class อื่นๆ ก็เปลี่ยนแปลงเล็กน้อย ด้วยการ extends จาก AbstractCSVReader เลย ดังนี้

3. ทำการ run unit test ที่เขียนไว้สิ จะพบว่ายังทำงานผ่านเช่นเดิมนะ เพราะว่า ผมทำการ Refactor code ไงล่ะ

Screen Shot 2557-08-11 at 11.25.57 AM

โดยสรุปแล้ว

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

โดย code ที่ซ้ำซ้อนมักจะเกิดขึ้นอยู่ในส่วน Data Access Object ( DAO )
ซึ่งมักจะมี method ทำงาน เช่น select, insert, update และ delete
แต่ละ DAO จะทำงานเหมือนกัน ต่างเพียงตารางข้อมูลที่กระทำเท่านั้นเอง
ดังนั้น เราสามารถลดความซ้ำซ้อนลงได้ด้วยวิธีการนี้

จะเห็นได้ว่า

เราควรที่จะทำความเข้าใจว่าปัญหาเราคืออะไร
แล้วจึงคิดต่อไปว่า เราจะแก้ไขด้วยวิธีการใด
ต่อจากนั้นให้ลงมือทำ …

และจะเห็นว่า code ที่ผมเขียนจะมี unit test คลุมเสมอนะ
ดังนั้น unit test เป็นสิ่งที่ขาดไม่ได้เลย สำหรับการ refactor code

สุดท้ายจริงๆ แล้ว นี่เป็นเพียงวิธีการหนึ่งในการแก้ไขปัญหารูปแบบนี้เท่านั้นนะครับ …
มันยังมีวิธีการอื่นๆ อีกมากมายครับ ลองศึกษากันดู

และพวก Design Pattern ต่างๆ นั้น อย่าจำ !!
แต่ให้ทำความเข้าใจว่ามันเกิดขึ้นมาเพื่อแก้ไขปัญหาอะไร

ตัวอย่างของ sourcecode อยู่ที่ Github :: Demo for using Template Method Pattern