Dalam sebuah proyek, saya telah menemukan kode seperti ini:
class SomeClass
{
private SomeType _someField;
public SomeType SomeField
{
get { return _someField; }
set { _someField = value; }
}
protected virtual void SomeMethod(/*...., */SomeType someVar)
{
}
private void SomeAnotherMethod()
{
//.............
SomeMethod(_someField);
//.............
}
};
Bagaimana saya meyakinkan rekan tim saya bahwa ini adalah kode yang buruk?
Saya percaya ini adalah komplikasi yang tidak perlu. Mengapa meneruskan variabel anggota sebagai parameter metode jika Anda sudah memiliki akses ke sana? Ini juga merupakan pelanggaran enkapsulasi.
Apakah Anda melihat ada masalah lain dengan kode ini?
Jawaban:
Saya pikir ini adalah topik yang valid, tetapi alasan Anda mendapat tanggapan yang beragam adalah karena cara pertanyaan itu dibentuk. Secara pribadi, saya memiliki pengalaman yang sama dengan tim saya di mana melewati anggota sebagai argumen tidak perlu dan berbelit-belit kode. Kami memiliki kelas yang bekerja dengan sekumpulan anggota tetapi beberapa fungsi mengakses anggota secara langsung dan fungsi lainnya akan memodifikasi anggota yang sama melalui parameter (yaitu menggunakan nama yang sama sekali berbeda) dan sama sekali tidak ada alasan teknis untuk melakukan itu. Secara teknis, maksud saya contoh yang diberikan Kate.
Saya akan merekomendasikan untuk mengambil langkah mundur dan alih-alih berfokus tepat pada kelulusan anggota sebagai parameter, mulailah diskusi dengan tim Anda tentang kejelasan dan keterbacaan. Entah lebih formal, atau hanya di lorong, membahas apa yang membuat beberapa segmen kode lebih mudah dibaca dan segmen kode lainnya lebih sulit. Kemudian identifikasi ukuran kualitas atau atribut kode bersih yang ingin Anda perjuangkan. Lagi pula, bahkan ketika mengerjakan proyek-proyek lapangan hijau, kami menghabiskan lebih dari 90% waktu membaca dan segera setelah kode ditulis (misalkan 10-15 menit kemudian) ia masuk ke pemeliharaan di mana keterbacaan bahkan lebih penting.
Jadi untuk contoh spesifik Anda di sini, argumen yang akan saya gunakan adalah bahwa lebih sedikit kode selalu lebih mudah dibaca daripada lebih banyak kode. Fungsi yang memiliki 3 parameter lebih sulit bagi otak untuk diproses daripada fungsi yang tidak memiliki atau 1 parameter. Jika ada nama variabel lain, otak harus melacak hal lain saat membaca kode. Jadi untuk mengingat "int m_value" dan kemudian "int localValue" dan ingat bahwa yang satu benar-benar berarti yang lain selalu lebih mahal untuk otak Anda daripada hanya bekerja dengan "m_value".
Untuk lebih banyak amunisi dan ide, saya akan merekomendasikan mengambil salinan Kode Paman Bob .
sumber
Saya bisa memikirkan pembenaran untuk meneruskan bidang anggota sebagai parameter dalam metode (pribadi): itu membuat eksplisit apa yang tergantung pada metode Anda.
Seperti yang Anda katakan, semua bidang anggota adalah parameter implisit dari metode Anda, karena seluruh objek. Namun, apakah objek lengkap benar-benar diperlukan untuk menghitung hasilnya? Jika
SomeMethod
merupakan metode internal yang hanya bergantung_someField
, bukankah lebih bersih untuk menjadikan ketergantungan ini eksplisit? Bahkan, membuat dependensi ini secara eksplisit juga mengisyaratkan bahwa Anda sebenarnya dapat memperbaiki kode ini dari kelas Anda! (Catatan saya berasumsi kita tidak berbicara tentang getter atau setter di sini, tetapi kode yang benar-benar menghitung sesuatu)Saya tidak akan membuat argumen yang sama untuk metode publik, karena penelepon tidak tahu atau peduli tentang bagian mana dari objek yang relevan untuk menghitung hasilnya ...
sumber
_someField
hanya parameter yang diperlukan untuk menghitung hasilnya, dan kami baru saja membuatnya eksplisit. (Catatan: ini penting. Kami tidak menambahkan ketergantungan, kami hanya membuatnya eksplisit!)this
(atauself
), tetapi dibuat eksplisit oleh panggilan itu sendiri,obj.method(x)
). Ketergantungan implisit lainnya adalah keadaan objek; ini biasanya membuat kode lebih sulit untuk dipahami. Kapan pun memungkinkan - dan sesuai alasan - buat dependensi eksplisit & fungsional-gaya. Dalam kasus metode pribadi, jika mungkin, secara eksplisit berikan setiap parameter yang mereka butuhkan. Dan ya, itu membantu untuk memperbaiki mereka.Saya melihat satu alasan kuat untuk meneruskan variabel anggota sebagai argumen fungsi ke metode pribadi - kemurnian fungsi. Variabel anggota secara efektif merupakan keadaan global dari sudut pandang, terlebih lagi, keadaan global yang dapat berubah jika anggota tersebut berubah selama eksekusi metode. Dengan mengganti referensi variabel anggota dengan parameter metode, kita dapat secara efektif membuat fungsi murni. Fungsi murni tidak bergantung pada keadaan eksternal dan tidak memiliki efek samping, selalu mengembalikan hasil yang sama dengan serangkaian parameter input yang sama - sehingga memudahkan pengujian dan pemeliharaan di masa mendatang.
Tentu tidak mudah atau praktis untuk memiliki semua metode Anda sebagai metode murni dalam bahasa OOP. Tapi saya percaya Anda mendapatkan banyak dalam hal kejelasan kode dengan memiliki metode yang menangani logika kompleks murni dan menggunakan variabel tidak berubah, sambil menjaga penanganan "global" keadaan tidak murni dipisahkan dalam metode khusus.
Melewati variabel anggota ke fungsi publik dari objek yang sama ketika memanggil fungsi secara eksternal akan tetapi merupakan bau kode utama menurut saya.
sumber
Jika fungsi ini dipanggil beberapa kali, kadang-kadang melewati variabel anggota itu dan kadang-kadang melewati sesuatu yang lain, maka tidak masalah. Misalnya, saya tidak akan menganggap ini buruk sama sekali:
di mana
newStartDate
beberapa variabel lokal danm_StartDate
merupakan variabel anggota.Namun jika fungsi hanya dipanggil dengan variabel anggota yang diteruskan ke sana, itu aneh. Fungsi anggota bekerja pada variabel anggota setiap saat. Mereka mungkin melakukan ini (tergantung pada bahasa tempat Anda bekerja) untuk mendapatkan salinan variabel anggota - jika itu masalahnya dan Anda tidak dapat melihat itu, kode mungkin lebih baik jika membuat seluruh proses eksplisit.
sumber
Yang tidak disentuh orang adalah SomeMethod dilindungi virtual. Berarti kelas turunan dapat menggunakannya DAN mengimplementasikan kembali fungsinya. Kelas turunan tidak akan memiliki akses ke variabel pribadi dan karenanya tidak bisa menyediakan implementasi kustom SomeMethod yang tergantung pada variabel pribadi. Alih-alih mengambil ketergantungan pada variabel pribadi, deklarasi mengharuskan penelepon untuk meneruskannya.
sumber
Kerangka kerja GUI biasanya memiliki semacam kelas 'Tampilan' yang mewakili hal-hal yang digambar di layar, dan kelas itu biasanya menyediakan metode seperti
invalidateRect(Rect r)
untuk menandai bagian dari area gambarnya yang perlu digambar ulang. Klien dapat memanggil metode itu untuk meminta pembaruan ke bagian tampilan. Tetapi suatu tampilan mungkin juga memanggil metode sendiri, seperti:menyebabkan menggambar ulang seluruh area. Misalnya, mungkin melakukan ini ketika pertama kali ditambahkan ke hierarki tampilan.
Tidak ada yang salah dengan melakukan ini - frame view adalah persegi panjang yang valid, dan view sendiri tahu bahwa ia ingin menggambar ulang dirinya sendiri. Kelas tampilan dapat menyediakan metode terpisah yang tidak menggunakan parameter dan menggunakan bingkai tampilan sebagai gantinya:
Tetapi mengapa menambahkan metode khusus hanya untuk ini ketika Anda dapat menggunakan yang lebih umum
invalidateRect()
? Atau, jika Anda memang memilih untuk menyediakaninvalidateFrame()
, kemungkinan besar Anda akan mengimplementasikannya dalam hal yang lebih umuminvalidateRect()
:Anda harus meneruskan variabel instan sebagai parameter ke metode Anda sendiri jika metode tidak beroperasi secara khusus pada variabel instan tersebut. Pada contoh di atas, bingkai view hanyalah persegi panjang sejauh
invalidateRect()
metode yang digunakan.sumber
Ini masuk akal jika metode ini adalah metode utilitas. Katakan misalnya Anda harus mendapatkan nama pendek yang unik dari beberapa string teks gratis.
Anda tidak ingin membuat kode implementasi yang terpisah untuk setiap string, alih-alih meneruskan string ke metode umum masuk akal.
Namun jika metode ini selalu bekerja pada satu anggota tampaknya agak bodoh untuk meneruskannya sebagai parameter.
sumber
Alasan utama untuk memiliki variabel anggota di kelas adalah untuk memungkinkan Anda mengaturnya di satu tempat, dan memiliki nilainya tersedia untuk setiap metode lain di kelas. Oleh karena itu secara umum Anda akan berharap bahwa tidak perlu melewatkan variabel anggota ke dalam metode kelas.
Namun saya dapat memikirkan beberapa alasan mengapa Anda mungkin ingin meneruskan variabel anggota ke metode lain di kelas. Yang pertama adalah jika Anda perlu menjamin bahwa nilai variabel anggota perlu digunakan tidak berubah ketika digunakan dengan metode yang dipanggil, bahkan jika metode itu perlu mengubah nilai variabel anggota aktual di beberapa titik dalam proses. Alasan kedua terkait dengan yang pertama di mana Anda mungkin ingin menjamin ketetapan nilai dalam lingkup rantai metode - ketika menerapkan sintaks yang lancar misalnya.
Dengan semua ini dalam pikiran, saya tidak akan mengatakan bahwa kode itu "buruk" jika Anda melewatkan variabel anggota ke salah satu metode kelasnya. Namun saya akan menyarankan bahwa itu umumnya tidak ideal, karena mungkin mendorong banyak duplikasi kode di mana parameter ditugaskan ke variabel lokal misalnya, dan parameter tambahan menambahkan "noise" dalam kode di mana itu tidak diperlukan. Jika Anda seorang penggemar buku Kode Bersih, Anda akan tahu bahwa itu menyebutkan bahwa Anda harus menjaga jumlah parameter metode ke minimum barest, dan hanya jika tidak ada cara lain yang lebih masuk akal untuk metode untuk mengakses parameter .
sumber
Beberapa hal yang datang kepada saya ketika memikirkan hal ini:
sumber
Anda hilang jika parameter ("argumen") adalah referensi atau hanya-baca.
Banyak pengembang, biasanya menetapkan langsung bidang internal variabel, dalam metode konstruktor. Ada beberapa pengecualian.
Ingat bahwa "setter" dapat memiliki metode tambahan, bukan hanya penetapan, dan kadang-kadang, bahkan metode virtual, atau memanggil metode virtual.
Catatan: Saya sarankan menjaga bidang internal properti sebagai "dilindungi".
sumber