Skip to content

Code Refactoring

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

คำถาม: เมื่อไหร่เราจะพูดได้ว่าระบบของเราจำเป็นต้องได้รับการ Refactor?
คำตอบ: เมื่อมี Technical Debt ในระบบมากเกินไป

Technical Debt คืออะไร?

Technical Debt เปรียบเหมือนการกู้เงินจากธนาคารที่ทำให้คุณสามารถซื้อของได้เร็วขึ้น แต่ คุณต้องจ่ายดอกเบี้ยทีหลัง

การเขียนโค้ดก็เช่นเดียวกัน คุณสามารถเร่งพัฒนาระบบได้โดยการข้าม Best Practices บางอย่างไปก่อน การทำแบบนั้นทำให้คุณส่งงานได้เร็วขึ้น แต่โค้ดที่ไม่ทำตาม Best Practices ย่อมมี "ดอกเบี้ย" ที่ต้องจ่าย โดย "ดอกเบี้ย" ในที่นี้คือทรัพยากรที่ต้องใช้เพิ่มขึ้นในการทำงานหรือจัดการกับโค้ดที่ไม่เป็นไปตาม Best Practices เหล่านั้น คุณจะเพิ่มฟีเจอร์ใหม่ แก้ไขปัญหา หรือดูแลรักษาระบบได้ช้าลงเรื่อยๆ ผลกระทบทางธุรกิจก็อาจเกิดขึ้น เช่น ความพอใจของลูกค้าลดลง สุดท้ายคุณจะไม่สามารถทนกับการจ่าย "ดอกเบี้ย" ที่สูงขนาดนั้นได้ และต้องยอม "จ่ายหนี้" ด้วยการเขียนระบบใหม่ทั้งหมดหรือทำการ Refactor ครั้งใหญ่

สาเหตุของ Technical Debt

Technical Debt เกิดได้จากหลายสาเหตุ เช่น

  • Business Pressure: เมื่อมีเวลาจำกัด คุณอาจเลือกใช้ solution ที่ทำได้เร็วแต่ไม่เป็นไปตาม best practices
  • Lack of understanding of technical debt consequences: ความไม่เข้าใจผลกระทบของ technical debt ทำให้ไม่เห็นความสำคัญที่จะจัดสรรเวลาให้ทีมจัดการ tech debt อย่างเหมาะสม
  • Failing to combat component coherence: โครงสร้างโค้ดที่เชื่อมโยงกันอย่างแน่นแฟ้นทำให้การเปลี่ยนแปลงในส่วนหนึ่งของโปรเจกต์อาจส่งผลกระทบต่อส่วนอื่นๆ จึงมีการหลีกเลี่ยงที่จะแตะโค้ดส่วนนั้นไปโดยปริยาย
  • Lack of tests: หากไม่มีการทดสอบรองรับ การเปลี่ยนแปลงเล็กๆ ก็อาจทำให้เกิดบั๊กใหญ่ในระบบได้ คนเลยหลีกเลี่ยงที่จะแตะโค้ดที่ขาด tests
  • Lack of documentation: การขาดเอกสารอธิบาย logic ทำให้การปรับปรุงระบบหรือการนำคนใหม่เข้าทีมยากขึ้น และอาจทำให้การพัฒนาชะงักหากบุคลากรสำคัญออกจากโปรเจกต์
  • Lack of interaction between team members: การสื่อสารไม่ดีในทีมทำให้คนในทีมมีข้อมูลที่ไม่ตรงกัน
  • Long-term simultaneous development in several branches: ทำให้สะสม technical debt และเพิ่มมากขึ้นเมื่อทำการรวมโค้ด
  • Delayed refactoring: ยิ่งเลื่อนการ Refactor ออกไป โค้ดยิ่งล้าสมัยและแก้ไขยากขึ้นเรื่อยๆ
  • Incompetence: นักพัฒนาอาจไม่รู้วิธีหรือไม่เชี่ยวชาญในการเขียนโค้ดที่มีคุณภาพ

วิธีการทำ Code Refactoring

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

โดยขั้นตอนการทำ code refactoring มีดังนี้

1. ตรวจหา Code Smells

มี code smells หลายแบบที่สามารถพบได้บ่อยๆเช่น

1.1 Bloaters: โค้ด เมธอด หรือคลาสที่ใหญ่จนเกินไป จนจัดการได้ยาก

  • Long Method: เมธอดที่มีโค้ดมากเกินไป โดยปกติเมธอดที่ยาวเกินกว่า 10 บรรทัดควรถูกตั้งคำถามแล้วว่าควรถูกแยกออกหรือไม่
  • Large Class: คลาสที่มีฟิลด์หรือเมธอดมากเกินไป
  • Primitive Obsession: การใช้ฟิลด์ primitive แทนการสร้างคลาสที่เหมาะสม
  • Long Parameter List: เมธอดที่มีพารามิเตอร์มากกว่า 3-4 ตัว
  • Data Clumps: กลุ่มของตัวแปรที่ใช้ร่วมกันในหลายส่วนของโค้ดควรถูกแยกออกมาเป็นคลาส

1.2 Object-Orientation Abusers: การใช้ OOP ไม่ถูกต้อง

  • Alternative Classes with Different Interfaces: คลาสสองคลาสที่ทำหน้าที่เหมือนกันแต่ใช้ interface ที่ต่างกัน
  • Refused Bequest: Subclass ใช้แค่บางส่วนของเมธอดหรือฟิลด์ที่สืบทอดมาจากคลาสหลัก
  • Switch Statements: มีการใช้ switch statement หรือ if statements ที่ซับซ้อนเกินไป
  • Temporary Field: ฟิลด์ชั่วคราวที่ใช้งานเฉพาะในบางสถานการณ์ และไม่มีประโยชน์ในสถานการณ์อื่นๆ

1.3 Change Preventers: โค้ดที่ทำให้คุณเมื่อคุณต้องไปแก้ไขในหลายๆ ส่วนพร้อมกันเมื่อต้องทำการเปลี่ยนแปลงจุดเดียว

  • Divergent Change: ต้องเปลี่ยนแปลงหลายๆ เมธอดที่ไม่เกี่ยวข้องกันเมื่อทำการเปลี่ยนแปลงคลาส
  • Shotgun Surgery: ต้องทำการเปลี่ยนแปลงเล็กๆ ในหลายๆ คลาสพร้อมกัน
  • Parallel Inheritance Hierarchies: การสร้าง subclass ใหม่จะต้องสร้าง subclass อื่นๆ เพิ่มด้วย

1.4 Dispensable: สิ่งที่ไม่จำเป็นและควรถูกกำจัดออกไปเพื่อทำให้โค้ดสะอาดขึ้น

  • Comments: เมธอดที่เต็มไปด้วยคอมเมนต์ ถ้าเขียนโค้ดให้ชัดเจนเพียงพอ คอมเมนต์ก็ไม่จำเป็น
  • Duplicate Code: โค้ดที่เหมือนกันหรือคล้ายกันในหลายส่วน
  • Lazy Class: คลาสที่มีขนาดเล็กมากหรือไม่มีประโยชน์
  • Dead Code: โค้ดที่ไม่ถูกใช้งาน
  • Speculative Generality: คลาส เมธอด ฟิลด์ หรือพารามิเตอร์ที่ไม่ได้ถูกใช้งาน

1.5 Couplers: การเชื่อมโยงระหว่างคลาสมากเกินไป

  • Feature Envy: เมธอดที่เข้าถึงข้อมูลจากคลาสอื่นมากกว่าข้อมูลของตัวเอง
  • Inappropriate Intimacy: คลาสหนึ่งที่ใช้ฟิลด์ภายในหรือเมธอดของอีกคลาส
  • Message Chains: การเรียกเมธอดต่อเนื่องกันหลายขั้นตอน เช่น $a->b()->c()->d()
  • Middleman: คลาสที่ทำหน้าที่เพียงแค่ส่งต่อการทำงานไปยังคลาสอื่น

1.6 Other Smells:

  • Incomplete Library Class: ไลบรารีที่ไม่สามารถตอบสนองความต้องการได้

2. ทำการ Refactor โดยใช้ Refactoring Techniques

การ Refactor สามารถทำได้หลายวิธี โดยมี 6 ประเภทหลัก ได้แก่:

  • การจัดการเมธอด
  • การย้ายฟีเจอร์ระหว่างคลาส
  • การจัดการข้อมูล
  • การทำให้เงื่อนไขง่ายขึ้น
  • การทำให้การเรียกเมธอดง่ายขึ้น
  • การจัดการ generalization

สามารถดูเทคนิคและตัวอย่างได้ใน Refactoring Cheatsheet คุณเปิ้ลผู้เขียนได้รวบรวมทั้งหมด 66 เทคนิคในการ refactor code ออกมาเป็น cheatsheet ไว้ด้านล่างสุดของบทความ คุณสามารถนำไปใช้ได้เลย

Reference