Cyclomatic Complexity

Cyclomatic Complexity (CC) เป็น metric ระดับโค้ดที่ออกแบบโดย Thomas McCabe Sr. ในปี 1976 เพื่อให้การวัดความซับซ้อนของโค้ดในระดับฟังก์ชัน/เมท็อด, คลาส หรือแอปพลิเคชันเป็นไปอย่างเป็นภววิสัย มันคำนวณโดยนำทฤษฎีกราฟมาประยุกต์ใช้กับโค้ด โดยเฉพาะ decision points ซึ่งทำให้เกิด execution path ที่แตกต่างกัน ตัวอย่างเช่น ถ้าฟังก์ชันหนึ่งไม่มี decision statement เลย (เช่น if statement) ก็จะได้ CC = 1 ถ้าฟังก์ชันมี conditional เดียว ก็จะได้ CC = 2 เพราะมี execution path ที่เป็นไปได้สองทาง

สูตรคำนวณ CC สำหรับฟังก์ชันหรือเมท็อดเดียวคือ C C \= E - N + 2 โดยที่ N แทน nodes (บรรทัดโค้ด) และ E แทน edges (การตัดสินใจที่เป็นไปได้) ลองดูโค้ดสไตล์ C ใน Example 6-1

Example 6-1. Sample code for Cyclomatic Complexity evaluation
public void decision(int c1, int c2) {
    if (c1 < 100)
        return 0;
    else if (c1 + C2 > 500)
       return 1;
    else
      return -1;
}

Cyclomatic Complexity ของ Example 6-1 คือ 3 (3 – 2 + 2) ตามที่แสดงใน Figure 6-1

fsa2 0601

Figure 6-1. Cyclomatic Complexity graph for the decision function

เลข 2 ที่ปรากฏในสูตร Cyclomatic Complexity เป็นการลดรูปสำหรับฟังก์ชัน/เมท็อดเดี่ยว สำหรับกรณี fan-out call ไปยังเมท็อดอื่น (เรียกว่า connected components ในทฤษฎีกราฟ) สูตรทั่วไปกว่าคือ C C \= E - N + 2 P โดยที่ P แทนจำนวน connected component

สถาปนิกและนักพัฒนาต่างเห็นตรงกันว่าโค้ดที่ซับซ้อนเกินไปคือ "code smell" — บางอย่างที่ปรากฏในโค้ดซึ่งแย่มากจนเหมือนมีกลิ่นในเชิงเปรียบเทียบ มันบั่นทอนแทบทุก characteristic ที่พึงประสงค์ของโค้ดเบส ไม่ว่าจะเป็น modularity, testability, deployability และอื่น ๆ ถ้าทีมไม่จับตาดูความซับซ้อนที่ค่อย ๆ เพิ่มขึ้น มันจะครอบงำโค้ดเบสในที่สุด

Cyclomatic Complexity เป็นตัวอย่างที่ดีของความหยาบของ metric ที่สถาปนิกมีให้ใช้ แม้มันจะวัดความซับซ้อนของโค้ดได้ แต่มันบอกไม่ได้ว่าความซับซ้อนนั้นเป็น essential complexity (เพราะเรากำลังแก้ปัญหาที่ซับซ้อนโดยธรรมชาติ) หรือ accidental complexity (เพราะเราออกแบบได้ไม่ดี) metric อย่าง CC มีประโยชน์มากในการประเมินโค้ด ไม่ว่าจะเขียนโดยนักพัฒนาหรือ generative AI ก็ตาม generative AI มักแก้ปัญหาด้วยวิธี brute force ซึ่งมักนำไปสู่ accidental complexity