Bau kode adalah gejala yang menunjukkan bahwa ada masalah dalam desain yang berpotensi meningkatkan jumlah bug: ini bukan kasus untuk daerah, tetapi daerah dapat berkontribusi menciptakan bau kode, seperti metode panjang.
Sejak:
Anti-pola (atau antipattern) adalah pola yang digunakan dalam operasi sosial atau bisnis atau rekayasa perangkat lunak yang mungkin umum digunakan tetapi tidak efektif dan / atau kontraproduktif dalam praktiknya
daerah adalah anti-pola. Mereka membutuhkan lebih banyak pekerjaan yang tidak meningkatkan kualitas atau keterbacaan kode, yang tidak mengurangi jumlah bug, dan yang hanya dapat membuat kode lebih rumit untuk diperbaiki.
Jangan gunakan daerah di dalam metode; refactor sebagai gantinya
Metode harus pendek . Jika hanya ada sepuluh baris dalam suatu metode, Anda mungkin tidak akan menggunakan daerah untuk menyembunyikan lima di antaranya saat mengerjakan lima lainnya.
Juga, setiap metode harus melakukan satu dan satu-satunya hal . Daerah, di sisi lain, dimaksudkan untuk memisahkan hal-hal yang berbeda . Jika metode Anda menggunakan A, maka B, masuk akal untuk membuat dua wilayah, tetapi ini adalah pendekatan yang salah; alih-alih, Anda harus mengubah metode menjadi dua metode terpisah.
Menggunakan wilayah dalam kasus ini juga dapat membuat refactoring lebih sulit. Bayangkan Anda memiliki:
private void DoSomething()
{
var data = LoadData();
#region Work with database
var verification = VerifySomething();
if (!verification)
{
throw new DataCorruptedException();
}
Do(data);
DoSomethingElse(data);
#endregion
#region Audit
var auditEngine = InitializeAuditEngine();
auditEngine.Submit(data);
#endregion
}
Meruntuhkan wilayah pertama untuk berkonsentrasi pada yang kedua tidak hanya berisiko: kita dapat dengan mudah melupakan pengecualian menghentikan aliran (mungkin ada klausa penjaga dengan return
sebaliknya, yang bahkan lebih sulit dikenali), tetapi juga akan memiliki masalah jika kode harus di refactored dengan cara ini:
private void DoSomething()
{
var data = LoadData();
#region Work with database
var verification = VerifySomething();
var info = DoSomethingElse(data);
if (verification)
{
Do(data);
}
#endregion
#region Audit
var auditEngine = InitializeAuditEngine(info);
auditEngine.Submit(
verification ? new AcceptedDataAudit(data) : new CorruptedDataAudit(data));
#endregion
}
Sekarang, wilayah tidak masuk akal, dan Anda tidak mungkin membaca dan memahami kode di wilayah kedua tanpa melihat kode di yang pertama.
Kasus lain yang kadang saya lihat adalah yang ini:
public void DoSomething(string a, int b)
{
#region Validation of arguments
if (a == null)
{
throw new ArgumentNullException("a");
}
if (b <= 0)
{
throw new ArgumentOutOfScopeException("b", ...);
}
#endregion
#region Do real work
...
#endregion
}
Sangat menggoda untuk menggunakan kawasan saat validasi argumen mulai menjangkau puluhan LOC, tetapi ada cara yang lebih baik untuk menyelesaikan masalah ini: yang digunakan oleh kode sumber .NET Framework:
public void DoSomething(string a, int b)
{
if (a == null)
{
throw new ArgumentNullException("a");
}
if (b <= 0)
{
throw new ArgumentOutOfScopeException("b", ...);
}
InternalDoSomething(a, b);
}
private void InternalDoSomething(string a, int b)
{
...
}
Jangan gunakan wilayah di luar metode untuk mengelompokkan
Beberapa orang menggunakannya untuk mengelompokkan bidang, properti, dll. Pendekatan ini salah: jika kode Anda sesuai dengan StyleCop, maka bidang, properti, metode pribadi, konstruktor, dll. Sudah dikelompokkan bersama dan mudah ditemukan. Jika tidak, maka saatnya untuk mulai berpikir tentang menerapkan aturan yang memastikan keseragaman di seluruh basis kode Anda.
Orang lain menggunakan daerah untuk menyembunyikan banyak entitas serupa . Misalnya, ketika Anda memiliki kelas dengan ratusan bidang (yang membuat setidaknya 500 baris kode jika Anda menghitung komentar dan spasi putih), Anda mungkin tergoda untuk meletakkan bidang-bidang itu di dalam suatu wilayah, menciutkannya, dan melupakannya. Sekali lagi, Anda salah melakukannya: dengan begitu banyak bidang dalam suatu kelas, Anda harus berpikir lebih baik tentang menggunakan warisan atau mengiris objek menjadi beberapa objek.
Akhirnya, beberapa orang tergoda untuk menggunakan daerah untuk mengelompokkan hal-hal terkait : peristiwa dengan delegasinya, atau metode yang berkaitan dengan IO dengan metode lain yang terkait dengan IO, dll. Dalam kasus pertama, itu menjadi berantakan yang sulit untuk dipertahankan , baca dan pahami. Dalam kasus kedua, desain yang lebih baik mungkin untuk membuat beberapa kelas.
Apakah ada gunanya untuk daerah?
Tidak. Ada warisan yang digunakan: kode yang dihasilkan. Namun, alat pembuat kode hanya harus menggunakan kelas parsial saja. Jika C # memiliki dukungan wilayah, itu sebagian besar karena warisan ini digunakan, dan karena sekarang terlalu banyak orang menggunakan wilayah dalam kode mereka, tidak mungkin untuk menghapusnya tanpa merusak basis kode yang ada.
Pikirkan tentang itu goto
. Fakta bahwa bahasa atau IDE mendukung fitur tidak berarti bahwa itu harus digunakan setiap hari. Aturan StyleCop SA1124 jelas: Anda tidak boleh menggunakan wilayah. Tidak pernah.
Contohnya
Saat ini saya sedang melakukan review kode dari kode rekan kerja saya. Basis kode berisi banyak daerah, dan sebenarnya adalah contoh sempurna dari kedua cara untuk tidak menggunakan daerah dan mengapa daerah menyebabkan kode buruk. Berikut ini beberapa contohnya:
4 000 monster LOC:
Saya baru-baru ini membaca di suatu tempat di Programmers.SE bahwa ketika sebuah file mengandung terlalu banyak using
(setelah menjalankan perintah "Hapus Penggunaan yang Tidak Digunakan"), itu adalah pertanda baik bahwa kelas di dalam file ini melakukan terlalu banyak. Hal yang sama berlaku untuk ukuran file itu sendiri.
Saat meninjau kode, saya menemukan 4 000 file LOC. Tampaknya penulis kode ini hanya menyalin-menyisipkan metode 15-baris yang sama ratusan kali, sedikit mengubah nama variabel dan metode yang disebut. Regex sederhana diizinkan untuk memangkas file dari 4.000 LOC menjadi 500 LOC, hanya dengan menambahkan beberapa obat generik; Saya cukup yakin bahwa dengan refactoring yang lebih pintar, kelas ini dapat dikurangi menjadi beberapa lusin baris.
Dengan menggunakan daerah, penulis mendorong dirinya untuk mengabaikan fakta bahwa kode tersebut tidak mungkin untuk dipertahankan dan ditulis dengan buruk, dan sangat menduplikasi kode alih-alih memperbaikinya.
Wilayah "Do A", Wilayah "Do B":
Contoh lain yang sangat baik adalah metode inisialisasi monster yang hanya melakukan tugas 1, lalu tugas 2, lalu tugas 3, dll. Ada lima atau enam tugas yang benar-benar independen, masing-masing menginisialisasi sesuatu dalam kelas wadah. Semua tugas itu dikelompokkan menjadi satu metode, dan dikelompokkan ke daerah.
Ini memiliki satu keuntungan:
- Metode ini cukup jelas untuk dipahami dengan melihat nama-nama wilayah. Ini dikatakan, metode yang sama sekali refactored akan sejelas yang asli.
Masalahnya, di sisi lain, beragam:
Tidak jelas apakah ada ketergantungan antar daerah. Mudah-mudahan, tidak ada penggunaan kembali variabel; jika tidak, pemeliharaan bisa menjadi mimpi buruk bahkan lebih.
Metode itu hampir tidak mungkin untuk diuji. Bagaimana Anda dengan mudah tahu jika metode yang melakukan dua puluh hal sekaligus melakukannya dengan benar?
Wilayah bidang, wilayah properti, wilayah konstruktor:
Kode yang ditinjau juga berisi banyak wilayah yang mengelompokkan semua bidang bersama-sama, semua properti bersama, dll. Ini memiliki masalah yang jelas: pertumbuhan kode sumber.
Saat Anda membuka file dan melihat daftar besar bidang, Anda lebih cenderung untuk refactor kelas terlebih dahulu, kemudian bekerja dengan kode. Dengan daerah, Anda memiliki kebiasaan untuk menghancurkan barang-barang dan melupakannya.
Masalah lain adalah bahwa jika Anda melakukannya di mana-mana, Anda akan menemukan diri Anda membuat satu blok wilayah, yang tidak masuk akal. Ini sebenarnya kasus dalam kode yang saya ulas, di mana ada banyak #region Constructor
berisi satu konstruktor.
Akhirnya, bidang, properti, konstruktor, dll. Harus sudah berurutan . Jika ya dan cocok dengan konvensi (konstanta dimulai dengan huruf kapital, dll.), Sudah jelas di mana pada jenis elemen berhenti dan lainnya dimulai, jadi Anda tidak perlu secara eksplisit membuat daerah untuk itu.