Kompleksitas siklis saat memanggil metode yang sama beberapa kali

12

Berkat pertanyaan di Code Review, saya sedikit berselisih (yang pada dasarnya adalah kesempatan untuk mempelajari sesuatu) tentang apa sebenarnya Kompleksitas Siklomatik untuk kode di bawah ini.

public static void main(String[] args) {
    try {
        thro();
        thro();
        thro();
        thro();
        thro();
        thro();
        thro();
    }
    catch (NullPointerException e) {
    }
}

private static Random random = new Random();

public static void thro() throws NullPointerException {
    if (random.nextBoolean())
        throw new NullPointerException();
    System.out.println("No crash this time");
}

Saat menulis kode ini di Eclipse dan menggunakan plugin Eclipse metrics , ia memberi tahu saya bahwa Kompleksitas McCabe Cyclomatic untuk metode utama adalah 2, dan untuk thrometode itu tertulis 2.

Namun, orang lain memberi tahu saya bahwa kompleksitas panggilan throbeberapa kali adalah number of calls * method complexity, dan karenanya mengklaim bahwa kompleksitas metode utama adalah 7 * 2 = 14.

Apakah kita mengukur hal yang berbeda? Bisakah kita berdua benar? Atau apakah sebenarnya kompleksitas siklomatik di sini?

Simon Forsberg
sumber
5
CC dari fungsinya adalah dua, karena hanya ada dua jalur. CC program lebih tinggi. Ini adalah tikaman lengkap dalam kegelapan, tetapi saya berasumsi perangkat lunak analisis kode mengambil setiap fungsi sebagai kotak hitam terpisah karena tidak layaknya menghitung CC dari seluruh aplikasi kompleks dalam sekali jalan.
Phoshi
@ Phoshi Jika Anda akan menulis itu sebagai jawaban dan (jika mungkin) memberikan tautan yang menunjukkan bahwa ada pemisahan keduanya, saya dengan senang hati menerima jawaban itu.
Simon Forsberg
Jika Anda menghitung semua jalur yang disebabkan oleh kemungkinan pengecualian dalam pengukuran CC, tuhan tolong orang yang mengajukan pertanyaan pada refactoring beberapa kode sepele untuk mendapatkan nomor di bawah 10.
mattnz

Jawaban:

9

Ketika saya memahami hal ini dengan benar, Cyclomatic Kompleksitas dari mainadalah 8 - yang adalah jumlah jalur independen linear melalui kode. Anda mendapatkan pengecualian di salah satu dari tujuh baris, atau tidak ada, tetapi tidak pernah lebih dari satu. Masing-masing "titik pengecualian" yang mungkin sesuai dengan satu jalur berbeda melalui kode.

Saya kira ketika McCabe menemukan metrik itu, ia tidak memiliki bahasa pemrograman dengan pengecualian dalam penanganannya.

Doc Brown
sumber
Tapi apakah itu benar-benar penting dari baris mana yang melempar pengecualian?
Simon Forsberg
5
@ SimonAndréForsberg: ya, benar. Pikirkan "thro" memiliki efek samping di mana ia menambah counter global ketika dipanggil (yang tidak akan mengubah jalur yang mungkin melalui kode). Kemungkinan hasil dari penghitung itu adalah 0 hingga 7, jadi ini membuktikan bahwa CC setidaknya 8.
Doc Brown
Apakah Anda akan mengatakan bahwa plugin metrik yang saya gunakan melaporkan nilai yang salah untuk mainmetode ini?
Simon Forsberg
@ SimonAndréForsberg: baik, saya tidak tahu plugin metrik Anda, tetapi 2 jelas bukan 8.
Doc Brown
Ada tautan ke plugin metrik dalam pertanyaan saya ....
Simon Forsberg
6

Menjadi 'lelaki lain', saya akan menjawab di sini, dan tepat tentang apa yang saya katakan (yang saya tidak terlalu tepat dengan formum lainnya)

Dengan menggunakan contoh kode di atas, saya menghitung kompleksitas siklomatik sebagai 8, dan saya memiliki komentar dalam kode untuk menunjukkan bagaimana saya menghitungnya. Untuk menggambarkan jalur saya akan mempertimbangkan loop sukses melalui semua yang thro()panggilan sebagai 'kode jalan 'utama'' (atau 'CP = 1'):

public static void main(String[] args) {
  try {
             // This is the 'main' Code Path: CP = 1
    thro();  // this has a branch, can succeed CP=1 or throw CP=2
    thro();  // this has a branch, can succeed CP=1 or throw CP=3
    thro();  // this has a branch, can succeed CP=1 or throw CP=4
    thro();  // this has a branch, can succeed CP=1 or throw CP=5
    thro();  // this has a branch, can succeed CP=1 or throw CP=6
    thro();  // this has a branch, can succeed CP=1 or throw CP=7
    thro();  // this has a branch, can succeed CP=1 or throw CP=8
  }
  catch (NullPointerException e) {
  }
}

Jadi, saya menghitung 8 jalur kode dalam metode utama ini, yang, bagi saya adalah Kompleksitas Cyclomatic dari 8.

Dalam istilah Java, setiap mekanisme untuk keluar dari suatu fungsi diperhitungkan dengan kompleksitasnya, jadi, sebuah metode yang memiliki status keberhasilan, dan, melempar, misalnya, mungkin hingga 3 pengecualian, memiliki 4 jalur keluar yang terdokumentasi.

Kompleksitas metode yang memanggil fungsi seperti itu, adalah:

CC(method) = 1 + sum (methodCallComplexity - 1)

Saya pikir hal-hal lain yang perlu dipertimbangkan, adalah bahwa, menurut pendapat saya, catchklausa tidak berkontribusi pada kompleksitas metode, catchitu hanyalah target dari throwscabang, dan dengan demikian merupakan blok penangkap yang menjadi target beberapa throwhitungan 1 kali untuk masing-masing throw, dan tidak hanya sekali untuk semuanya.

rolfl
sumber
Apakah Anda menghitung cabang yang mungkin untuk OutOfMemoryExceptions juga? Maksud saya dengan pedih mereka dapat menyebabkan kode cabang, tetapi tidak ada yang menghitungnya karena mereka mengencerkan kegunaan metrik.
Telastyn
Tidak, saya tidak ... dan Anda benar, tetapi, dalam konteks argumen ini, saya hanya menghitung pengecualian yang dinyatakan metode untuk dibuang. Juga, jika suatu metode mendeklarasikan tiga pengecualian, tetapi kode callinch melakukan a catch (Throwable t) {...maka saya pikir itu tidak masalah berapa banyak pengecualian yang dinyatakannya untuk dibuang.
rolfl