Skip to main content
•5 dk okuma

Kod Karmaşasını Çözmenin Anahtarı: Cyclomatic Complexity

Mustafa Kürşad BaşerMustafa Kürşad BAŞER
••
Kod Karmaşasını Çözmenin Anahtarı: Cyclomatic Complexity

Cyclomatic Complexity Nedir?

Hepimiz temiz ve okunabilir kod yazmak isteriz. Ama proje büyüdükçe fonksiyonlar sessizce şişer. Bir noktada bir metoda bakıp "bunu değiştirsem neyi bozarım" diye tereddüt edersiniz. Cyclomatic Complexity, o tereddüdü bir sayıya çeviren metrik.

Metriği 1976’da Thomas J. McCabe, "A Complexity Measure" başlıklı makalesinde tanımladı. O yüzden literatürde McCabe karmaşıklığı olarak da geçer.

Ölçtüğü şey satır sayısı değil, dallanma. Fonksiyonun içinden geçen bağımsız yol sayısını verir. Bu yolları if, while, for, case, catch ve && gibi kısa devre operatörleri açar. Yüz satırlık düz bir fonksiyonun değeri 1’dir; on satırlık ama beş dallanmalı bir fonksiyonunki 6.

Cyclomatic Complexity Formülü

M = E - N + 2P

M = Cyclomatic Complexity

E = kontrol akış grafiğindeki kenar sayısı

N = kontrol akış grafiğindeki düğüm sayısı

P = bağlantılı bileşenlerin sayısı

Formülün anlamı ise şu şekildedir:

Yani M, kenar sayısından düğüm sayısı çıkarılıp bağlantılı bileşen sayısının iki katı eklenerek bulunur. Grafik çizmek zorunda değilsiniz: tek bir fonksiyon için formül pratikte "karar noktası sayısı + 1" ifadesine indirgenir. Aşağıdaki örneklerde bu kısayolu kullanacağım.

Geeksforgeeks
["Cyclomatic Complexity", Geeksforgeeks](https://www.geeksforgeeks.org/cyclomatic-complexity/)

Yukarıdaki kod için hesaplanan Cyclomatic Complexity, kontrol akış grafiğinden elde edilir. Grafik yedi şekil (node) ve yedi çizgi (kenar/edge) gösteriyor. Dolayısıyla, Cyclomatic Complexity 7-7+2 = 2 olacaktır.

Cyclomatic Complexity'nin önemi nedir?

Metriğin en pratik faydası test tarafında, ama sık yanlış aktarılan bir yönü var. Cyclomatic Complexity, bütün dalları gezmek için gereken test sayısının üst sınırını verir; asgarisini değil. Peş peşe iki bağımsız if içeren bir fonksiyonun değeri 3’tür, oysa iki test bütün dalları kapatır. Yani "değeri 8, demek ki 8 test lazım" okuması yanlış olur; doğrusu "8’den fazlasına ihtiyacın olmayacak". Ters yönden bakınca yine işe yarar: değeri 12 olan bir fonksiyonu tek testle geçtiyseniz kapatılmamış dal olduğu neredeyse kesindir. McCabe’in kendi önerisi fonksiyon başına 10 sınırıydı. Sektörde yaygın okuma da şu yönde: 1 ile 10 arası rahat, 11 ile 20 arası dikkat, 20 üzeri ise bölünmesi gereken bir fonksiyon.

Metriğin kör noktası da var. Yalnızca dallanma sayar, iç içe geçmeyi umursamaz. Peş peşe dizilmiş dört if ile dört kat iç içe geçmiş dört if aynı puanı alır, oysa ikincisini okumak çok daha zordur. Bu yüzden sonradan Cognitive Complexity gibi iç içe geçmeyi cezalandıran metrikler önerildi. Cyclomatic Complexity’yi tek ölçüt değil, ilk uyarı sinyali olarak kullanın.

Cyclomatic Complexity'nin nasıl hesaplandığını basit bir fonksiyon ile örneklendirelim (Java):

java
public class NumberChecker {  public static void main(String[] args) {    int number = 10;    isEven(number);  }   public static void isEven(int number) {    if (number % 2 == 0) {      System.out.println("Even");    } else {      System.out.println("Odd");    }  }}

Yukarıdaki NumberChecker sınıfında isEven metodu bir sayının çift mi tek mi olduğunu yazdırıyor. Karar noktalarını sayalım: ortada tek bir if var. Buradaki else yeni bir karar açmaz, sadece mevcut kararın diğer dalıdır. Dolayısıyla 1 karar noktası, artı 1 eder ve M = 2 çıkar. Bu, yazının başındaki grafik hesabıyla da örtüşür: 7 - 7 + 2 = 2.

Başka bir örnek daha verecek olursam (Java):

java
public void calculateGrade(int score) {  if (score >= 90) {    System.out.println("A");  } else if (score >= 80) {    System.out.println("B");  } else if (score >= 70) {    System.out.println("C");  } else if (score >= 60) {    System.out.println("D");  } else {    System.out.println("F");  }}

Bu örnekte de basit bir "calculateGrade" fonksiyonumuz var, girdi olarak puan alıyor ve bu puana karşılık gelen harf notunu ekrana yazdırıyor. Bu örnekte, score adlı bir değişkenin değerine göre öğrencinin aldığı notu belirliyoruz.

İlk if ifadesi, score'un 90 veya daha büyük olup olmadığını kontrol eder. Eğer bu koşul sağlanırsa, "A" notunu ekrana basar. Eğer sağlanmazsa, bir sonraki else if ifadesine geçer ve score'un 80 veya daha büyük olup olmadığını kontrol eder. Bu şekilde devam eder ve her bir else if ifadesi, bir sonraki not aralığını kontrol eder. Son olarak, hiçbir koşul sağlanmazsa else bloğu çalışır ve "F" notunu ekrana basar.

Yine karar noktalarını sayıyoruz. Metotta dört koşul var: bir if ve üç else if. Sondaki else bir koşul içermediği için sayılmaz, o yalnızca geriye kalan durumu karşılar. Dört karar noktası artı bir, yani M = 5. En sık yapılan hata else bloğunu da saymak; öyle yaparsanız sonucu her zaman bir fazla bulursunuz.

Sonuç

Cyclomatic Complexity, sonuçta bir fonksiyondan geçen bağımsız yol sayısı ve hesabı çoğu zaman "karar noktalarını say, bir ekle" kadar basittir. Size iki şey verir: fonksiyonu tam kapatmak için kaç test yazmanız gerektiği, ve hangi metodun bölünme vaktinin geldiği. Elle hesaplamanız da gerekmez; SonarQube, ESLint’in complexity kuralı ve çoğu IDE bunu zaten raporluyor. Asıl mesele sayıyı üretmek değil, yükseldiğinde ne yapacağınızı bilmek. Cevap genellikle aynı: fonksiyonu bölün.

Referanslar

Bu yazıyı paylaş

Link kopyalandı!
Mustafa Kürşad Başer
Yazar

Mustafa Kürşad Başer

Kıdemli Yazılım Mühendisi

Karmaşık sorunlara zarif çözümler üretmekten keyif alan, tutkulu bir yazılım mühendisi. Kodlamanın ötesinde, teknoloji, sanat ve insan bilincinin kesişim noktalarını keşfetmekle derinden ilgileniyor.