Cara paling ramah manusia untuk memesan definisi metode kelas?

37

Dalam setiap definisi kelas yang diberikan, saya telah melihat definisi metode yang diurutkan dalam berbagai cara: abjad, kronologis berdasarkan penggunaan yang paling umum, abjad dikelompokkan berdasarkan visibilitas, abjad dengan getter dan setter dikelompokkan bersama, dll. Ketika saya mulai menulis kelas baru, Saya cenderung mengetik semuanya, lalu menyusun ulang ketika saya selesai menulis seluruh kelas. Pada catatan itu, saya punya tiga pertanyaan:

  1. Apakah pesanan itu penting?
  2. Apakah ada pesanan "terbaik"?
  3. Saya kira tidak ada, jadi apa pro dan kontra dari strategi pemesanan yang berbeda?
Johntron
sumber
1
Anda tidak benar-benar berharap orang untuk memotong / menempelkan kode hanya untuk menyusun ulang metode. Jika IDE melakukannya secara otomatis, maka boleh. Kalau tidak, sulit untuk ditegakkan.
Reactgular
Untuk memperjelas, pertanyaan saya adalah tentang keterbacaan / kegunaan, bukan sintaksis.
Johntron

Jawaban:

51

Dalam beberapa bahasa pemrograman, ketertiban memang penting karena Anda tidak dapat memanfaatkan berbagai hal sampai setelah dinyatakan. Tetapi kecuali itu, untuk sebagian besar bahasa itu tidak masalah bagi kompiler. Jadi, Anda tinggal membiarkannya menjadi masalah bagi manusia.

Kutipan favorit Martin Fowler saya adalah: Any fool can write code that a computer can understand. Good programmers write code that humans can understand.Jadi saya akan mengatakan bahwa pemesanan kelas Anda harus bergantung pada apa yang membuatnya mudah dipahami manusia.

Saya pribadi lebih suka perawatan step-down yang diberikan Bob Martin dalam Clean Codebukunya. Variabel anggota di bagian atas kelas, lalu konstruktor, lalu semua metode lainnya. Dan Anda memesan metode untuk berdekatan dengan bagaimana mereka digunakan dalam kelas (daripada secara sewenang-wenang menempatkan semua publik kemudian pribadi kemudian dilindungi). Dia menyebutnya meminimalkan "jarak vertikal" atau sesuatu seperti itu (saat ini tidak ada buku tentang saya).

Edit:

Ide dasar "jarak vertikal" adalah Anda ingin menghindari membuat orang melompat-lompat di sekitar kode sumber Anda hanya untuk memahaminya. Jika ada yang terkait, mereka harus lebih dekat bersama. Hal-hal yang tidak terkait dapat terpisah lebih jauh.

Bab 5 dari Kode Bersih (buku bagus, btw) membahas banyak detail tentang bagaimana Tn. Martin menyarankan kode pemesanan. Dia menyarankan bahwa membaca kode harus bekerja seperti membaca artikel surat kabar: detail tingkat tinggi didahulukan (di bagian atas) dan Anda mendapatkan lebih banyak detail saat membaca. Dia berkata, "Jika satu fungsi memanggil fungsi lainnya, fungsi itu harus ditutup secara vertikal, dan si penelepon harus berada di atas callee, jika memungkinkan." Selain itu, konsep terkait harus berdekatan.

Jadi, inilah contoh yang dibuat-buat yang buruk dalam banyak hal (desain OO yang buruk; tidak pernah menggunakan doubleuang) tetapi menggambarkan ide:

public class Employee {
  ...
  public String getEmployeeId() { return employeeId; }
  public String getFirstName() { return firstName; }
  public String getLastName() { return lastName; }

  public double calculatePaycheck() {
    double pay = getSalary() / PAY_PERIODS_PER_YEAR;
    if (isEligibleForBonus()) {
      pay += calculateBonus();
    }
    return pay;
  }

  private double getSalary() { ... }

  private boolean isEligibleForBonus() {
    return (isFullTimeEmployee() && didCompleteBonusObjectives());
  }

  public boolean isFullTimeEmployee() { ... }
  private boolean didCompleteBonusObjectives() { ... }
  private double calculateBonus() { ... }
}

Metode diperintahkan sehingga mereka dekat dengan yang memanggil mereka, bekerja turun dari atas. Jika kita meletakkan semua privatemetode di bawah ini public, maka Anda harus melakukan lebih banyak lagi untuk mengikuti alur program.

getFirstNamedan getLastNamesecara konseptual terkait (dan getEmployeeIdmungkin juga), sehingga mereka saling berdekatan. Kita bisa memindahkan semuanya ke bawah, tetapi kita tidak ingin melihat getFirstNamedi atas dan getLastNamedi bawah.

Semoga ini memberi Anda ide dasar. Jika Anda tertarik pada hal semacam ini, saya sangat merekomendasikan membaca Clean Code.

Allan
sumber
Saya perlu tahu bagaimana setter dan getter dari variabel instan harus ditempatkan. Haruskah itu datang tepat setelah konstruktor kelas atau di bagian bawah kelas?
srinivas
Secara pribadi saya suka mereka di atas setelah konstruktor. Tapi itu tidak terlalu penting; Saya akan merekomendasikan konsistensi dalam proyek Anda dan dengan tim Anda sebagai cara yang baik untuk memutuskan.
Allan
Bukankah seharusnya calculateBonus()datang sebelum isFullTimeEmployee()dan didCompleteBonusObjectives()?
winklerrr
@ winklerrr Saya bisa melihat argumen untuk itu. Saya menempatkan mereka di mana saya melakukannya karena isFullTimeEmployeedan didCompleteBonusObjectivesdigunakan oleh isEligibleForBonuskarena itu mereka harus dekat secara vertikal. Tapi Anda bisa berpotensi calculateBonusnaik untuk mendekatkannya ke tempat namanya. Sayangnya, karena Anda memiliki fungsi memanggil fungsi pada akhirnya Anda berakhir dengan masalah (seperti satu fungsi bersama yang dipanggil oleh beberapa orang lain) di mana tidak ada pemesanan yang sempurna. Kemudian terserah penilaian terbaik Anda. Saya merekomendasikan agar kelas dan fungsi tetap kecil untuk mengurangi masalah tersebut.
Allan
2

Saya biasanya memesan metode saya berdasarkan hubungan dan urutan penggunaan.

Ambil kelas jaringan, saya akan mengelompokkan semua metode TCP bersama, lalu semua metode UDP bersama-sama. Di dalam TCP saya akan memiliki metode setup sebagai yang pertama, mungkin mengirim pesan yang diberikan kedua dan tutup tcp socket sebagai yang ketiga.

Jelas tidak semua kelas akan cocok dengan pola itu, tapi itu adalah alur kerja umum saya.

Saya melakukannya dengan cara itu untuk debugging lebih dari apa pun, ketika saya memiliki masalah dan saya ingin melompat ke metode, saya tidak berpikir bagaimana itu dieja, saya pikir apa yang bertanggung jawab untuk dan pergi ke bagian itu.

Cara ini khususnya masuk akal bagi pihak ketiga melihat / menggunakan kode Anda sebagai dikelompokkan bersama dan akan mengikuti urutan yang digunakan, sehingga kode yang mereka tulis dengan kelas Anda akan mengikuti struktur yang sama dengan kelas.

Mengenai apakah itu penting?

untuk keterbacaan, pasti.

selain itu tidak juga, hanya dalam kasus di mana bahasa tertentu Anda tidak dapat memanggil metode kecuali ditentukan di atas di mana disebut dll.

Simon McLoughlin
sumber
0

Idealnya, kelas Anda sangat kecil sehingga tidak masalah. Jika Anda hanya memiliki selusin metode, dan editor atau IDE Anda mendukung pelipatan, Anda tidak memiliki masalah, karena seluruh daftar metode cocok dalam 12 baris.

Kegagalan itu, perbedaan tingkat atas harus publik vs swasta. Daftarkan metode publik terlebih dahulu: inilah yang paling dicari orang, karena mereka menentukan cara kelas Anda berinteraksi dengan basis kode lainnya.

Kemudian, di dalam masing-masing ini, masuk akal untuk mengelompokkan metode berdasarkan fungsi: konstruktor dan destruktor dalam satu blok, pengambil / penyetel di blok lain, operator kelebihan beban, metode statis, dan sisanya dari kelompok itu. Dalam C ++, saya ingin tetap operator=dekat dengan konstruktor, karena itu terkait erat dengan copy constructor, dan juga karena saya ingin dapat dengan cepat mengetahui apakah semua (atau tidak ada) dari default ctor, copy ctor, operator = dan dtor ada.

tammmer
sumber
-1

tl; dr

Hanya jika bahasa yang Anda gunakan memerlukan pemesanan khusus. Selain itu, pemesanan terserah Anda. Pilih sistem yang konsisten dan masuk akal, dan cobalah untuk tetap menggunakannya sebanyak mungkin.


1. Apakah pesanan itu penting?

Hanya jika bahasa yang Anda gunakan perlu memiliki fungsi yang ditentukan lebih awal dalam file daripada di mana ia dipanggil, seperti dalam contoh ini:

void funcA()
{
   funcB();
}

void funcB()
{
   //do something interesting...
}

Anda akan mendapatkan kesalahan karena Anda menelepon funcB()sebelum menggunakannya. Saya pikir ini adalah masalah dalam PL / SQL dan mungkin di C juga, tetapi Anda dapat memiliki deklarasi maju seperti:

void funcB();

void funcA()
{
   funcB();
}

void funcB()
{
   //do something interesting...
}

Ini satu-satunya situasi yang bisa saya pikirkan di mana jika pemesanannya "salah", Anda bahkan tidak akan bisa mengkompilasi.

Jika tidak, Anda selalu dapat memesan ulang sesuka Anda. Anda bahkan dapat menulis alat untuk melakukannya untuk Anda (jika Anda tidak dapat menemukannya di sana).

2. Apakah ada pesanan "terbaik"?

Jika bahasa / lingkungan tidak memiliki persyaratan pemesanan, maka urutan "terbaik" adalah yang paling cocok untuk Anda . Bagi saya, saya suka memiliki semua getter / setter bersama, biasanya di awal kelas (tapi setelah konstruktor / initializers statis), dan kemudian metode pribadi, lalu dilindungi, lalu publik. Dalam setiap grup berbasis lingkup biasanya tidak ada pemesanan, meskipun metode kelebihan beban saya coba untuk tetap bersama, dalam urutan jumlah parameter. Saya juga mencoba menyatukan metode dengan fungsi terkait, meskipun kadang-kadang saya harus memecah pemesanan berbasis lingkup untuk melakukannya; dan kadang-kadang mencoba mempertahankan pemesanan berbasis-ruang-fungsi-kelompok istirahat. Dan IDE saya dapat memberi saya tampilan garis besar alfabet, jadi itu bagus juga. ;)

Beberapa bahasa, seperti C # memiliki kemampuan untuk mengelompokkan kode dalam "wilayah" yang tidak berpengaruh pada kompilasi tetapi mungkin membuatnya lebih mudah untuk menjaga fungsi terkait bersama, kemudian menyembunyikan / menampilkannya dengan IDE. Seperti yang ditunjukkan MainMa , ada beberapa yang menganggap ini sebagai praktik buruk. Saya telah melihat contoh yang baik dan buruk dari daerah yang digunakan dengan cara ini, jadi jika Anda akan melakukannya, berhati-hatilah.

FrustratedWithFormsDesigner
sumber