Bab Paman Bob tentang nama dalam Kode Bersih merekomendasikan agar Anda menghindari penyandian nama, terutama yang berkaitan dengan notasi Hongaria. Dia juga secara spesifik menyebutkan menghapus I
awalan dari antarmuka, tetapi tidak menunjukkan contoh ini.
Mari kita asumsikan sebagai berikut:
- Penggunaan antarmuka terutama untuk mencapai testabilitas melalui injeksi ketergantungan
- Dalam banyak kasus, ini mengarah pada memiliki antarmuka tunggal dengan implementer tunggal
Jadi, misalnya, apa nama keduanya? Parser
dan ConcreteParser
? Parser
dan ParserImplementation
?
public interface IParser {
string Parse(string content);
string Parse(FileInfo path);
}
public class Parser : IParser {
// Implementations
}
Atau haruskah saya mengabaikan saran ini dalam kasus implementasi tunggal seperti ini?
c#
naming
clean-code
naming-standards
Vinko Vrsalovic
sumber
sumber
Jawaban:
Sementara banyak, termasuk "Paman Bob", menyarankan untuk tidak menggunakan
I
awalan untuk antarmuka, melakukannya adalah tradisi mapan dengan C #. Secara umum, itu harus dihindari. Tetapi jika Anda menulis C #, Anda harus benar-benar mengikuti konvensi bahasa itu dan menggunakannya. Tidak melakukannya akan menyebabkan kebingungan besar dengan orang lain yang akrab dengan C # yang mencoba membaca kode Anda.sumber
Antarmuka adalah konsep logis yang penting, oleh karena itu, antarmuka harus membawa nama generik. Jadi, saya lebih suka
dari
Yang terakhir memiliki beberapa isue:
sumber
Default
atauReal
atauImpl
dalam banyak nama kelas sayaIList<T>
dan tidak terlalu peduli bagaimana itu disimpan secara internal; bisa hanya lop dariI
dan memiliki nama kelas yang dapat digunakan tampaknya lebih baik daripada harus tahu secara ajaib (seperti di Jawa) bahwa kode yang ingin implementasi biasaList<T>
harus digunakanArrayList<T>
.CFoo : Foo
. Jadi opsi itu tidak tersedia. Dua, Anda mendapatkan inkonsistensi aneh karena beberapa kelas akan memiliki penanda dan yang lainnya tidak, tergantung pada apakah mungkin ada implementasi lain dari antarmuka yang sama.CFoo : Foo
tapiSqlStore : Store
. Itulah satu masalah yang saya milikiImpl
, yang pada dasarnya hanya merupakan penanda yang lebih buruk. Mungkin jika ada bahasa dengan konvensi yang selalu menambahkanC
(atau apa pun), itu mungkin lebih baik daripadaI
Ini bukan hanya tentang konvensi penamaan. C # tidak mendukung multiple inheritance sehingga penggunaan notasi Hungaria yang lama ini memiliki sedikit manfaat meskipun Anda mewarisi dari kelas dasar dan mengimplementasikan satu atau lebih antarmuka. Jadi ini...
... lebih disukai daripada ini ...
IMO
sumber
inherits
vsimplements
.extends
.Tidak tidak Tidak.
Konvensi penamaan bukan merupakan fungsi fandom Paman Bob Anda. Mereka bukan fungsi dari bahasa, c # atau sebaliknya.
Mereka adalah fungsi dari basis kode Anda. Untuk tingkat yang jauh lebih rendah dari toko Anda. Dengan kata lain yang pertama dalam basis kode menetapkan standar.
Hanya dengan begitu Anda bisa memutuskan. Setelah itu konsisten saja. Jika seseorang mulai tidak konsisten mengambil senjata nerf mereka dan membuat mereka memperbarui dokumentasi sampai mereka bertobat.
Saat membaca basis kode baru jika saya tidak pernah melihat
I
awalan saya baik-baik saja, terlepas dari bahasa. Jika saya melihat satu di setiap antarmuka saya baik-baik saja. Jika saya terkadang melihatnya, seseorang akan membayar.Jika Anda menemukan diri Anda dalam konteks yang jarang dijumpai dalam menetapkan preseden ini untuk basis kode Anda, saya mendorong Anda untuk mempertimbangkan ini:
Sebagai kelas klien saya berhak untuk tidak peduli dengan apa yang saya bicarakan. Yang saya butuhkan hanyalah nama dan daftar hal yang bisa saya panggil terhadap nama itu. Itu mungkin tidak akan berubah kecuali jika saya mengatakannya. SAYA SENDIRI bagian itu. Apa yang saya bicarakan, antarmuka atau tidak, bukan departemen saya.
Jika Anda menyelinap
I
dalam nama itu, klien tidak peduli. Jika tidak adaI
, klien tidak peduli. Apa yang dibicarakannya mungkin konkret. Mungkin tidak. Karena tidak memanggilnyanew
, tidak ada bedanya. Sebagai klien, saya tidak tahu. Saya tidak ingin tahu.Sekarang sebagai monyet kode melihat kode itu, bahkan di java, ini mungkin penting. Lagi pula, jika saya harus mengubah implementasi ke antarmuka saya bisa melakukannya tanpa memberi tahu klien. Jika tidak ada
I
tradisi saya mungkin tidak akan mengganti namanya. Yang mungkin menjadi masalah karena sekarang kita harus mengkompilasi ulang klien karena meskipun sumbernya tidak peduli biner melakukannya. Sesuatu yang mudah dilewatkan jika Anda tidak pernah mengubah namanya.Mungkin itu bukan masalah bagi Anda. Mungkin tidak. Jika ya, itu alasan yang bagus untuk peduli. Tapi untuk cinta mainan meja jangan mengklaim kita harus membabi buta melakukan ini karena itu cara yang dilakukan di beberapa bahasa. Entah memiliki alasan yang kuat atau tidak peduli.
Ini bukan tentang hidup dengannya selamanya. Ini tentang tidak memberi saya omong kosong tentang basis kode yang tidak sesuai dengan mentalitas khusus Anda kecuali jika Anda siap untuk memperbaiki 'masalah' di mana-mana. Kecuali Anda memiliki alasan yang kuat saya tidak seharusnya mengambil jalan yang paling tidak menentang keseragaman, jangan datang kepada saya untuk mengkhotbahkan konformitas. Aku punya hal yang lebih baik untuk dilakukan.
sumber
I
. Jika kode Anda menggunakannya, Anda akan melihatnya di mana-mana. Jika kode Anda tidak menggunakannya, Anda akan melihatnya dari kode kerangka kerja, mengarah tepat ke campuran yang tidak Anda inginkan.Ada satu perbedaan kecil antara Java dan C # yang relevan di sini. Di Jawa, setiap anggota adalah virtual secara default. Dalam C #, setiap anggota disegel secara default - kecuali untuk anggota antarmuka.
Asumsi yang sesuai dengan pedoman ini memengaruhi - di Jawa, setiap tipe publik harus dianggap non-final, sesuai dengan Prinsip Pergantian Liskov [1]. Jika Anda hanya memiliki satu implementasi, Anda akan memberi nama kelas
Parser
; jika Anda membutuhkan beberapa implementasi, Anda hanya akan mengubah kelas menjadi antarmuka dengan nama yang sama, dan mengganti nama implementasi konkret menjadi sesuatu yang deskriptif.Dalam C #, asumsi utama adalah bahwa ketika Anda mendapatkan kelas (nama tidak dimulai dengan
I
), itulah kelas yang Anda inginkan. Pikiran Anda, ini tidak mendekati 100% akurat - contoh-contoh khas akan menjadi kelas sepertiStream
(yang seharusnya merupakan antarmuka, atau beberapa antarmuka), dan setiap orang memiliki panduan dan latar belakang mereka sendiri dari bahasa lain. Ada juga pengecualian lain sepertiBase
sufiks yang cukup banyak digunakan untuk menunjukkan kelas abstrak - seperti halnya dengan antarmuka, Anda tahu jenisnya seharusnya polimorfik.Ada juga fitur kegunaan yang bagus dalam membiarkan nama non-I-awalan untuk fungsi yang berhubungan dengan antarmuka itu tanpa harus menggunakan membuat antarmuka kelas abstrak (yang akan merugikan karena kurangnya kelas multiple-inheritance di C #). Ini dipopulerkan oleh LINQ, yang digunakan
IEnumerable<T>
sebagai antarmuka, danEnumerable
sebagai tempat penyimpanan metode yang berlaku untuk antarmuka itu. Ini tidak perlu di Java, di mana antarmuka dapat berisi implementasi metode juga.Pada akhirnya,
I
awalan secara luas digunakan di dunia C #, dan dengan ekstensi, dunia .NET (karena sejauh ini sebagian besar. Kode NET ditulis dalam C #, masuk akal untuk mengikuti pedoman C # untuk sebagian besar antarmuka publik). Ini berarti Anda hampir pasti akan bekerja dengan perpustakaan dan kode yang mengikuti notasi ini, dan masuk akal untuk mengadopsi tradisi untuk mencegah kebingungan yang tidak perlu - tidak seperti menghilangkan awalan akan membuat kode Anda lebih baik :)Saya berasumsi bahwa alasan Paman Bob adalah sesuatu seperti ini:
IBanana
adalah gagasan abstrak pisang. Jika ada kelas pelaksana yang tidak memiliki nama yang lebih baik dari ituBanana
, abstraksi sama sekali tidak berarti, dan Anda harus meninggalkan antarmuka dan hanya menggunakan kelas. Jika ada nama yang lebih baik (katakanlah,LongBanana
atauAppleBanana
), tidak ada alasan untuk tidak menggunakanBanana
nama antarmuka. Oleh karena itu, menggunakanI
awalan menandakan bahwa Anda memiliki abstraksi yang tidak berguna, yang membuat kode lebih sulit untuk dipahami tanpa manfaat. Dan karena OOP yang ketat akan membuat Anda selalu kode terhadap antarmuka, satu-satunya tempat di mana Anda tidak akan melihatI
awalan pada suatu jenis akan berada di konstruktor - kebisingan yang tidak ada gunanya.Jika Anda menerapkan ini pada
IParser
antarmuka sampel Anda, Anda dapat dengan jelas melihat bahwa abstraksi sepenuhnya berada di wilayah "tidak berarti". Entah ada sesuatu yang spesifik tentang implementasi konkret dari parser (misalnyaJsonParser
,XmlParser
, ...), atau Anda hanya harus menggunakan kelas. Tidak ada yang namanya "implementasi default" (meskipun di beberapa lingkungan, ini memang masuk akal - terutama, COM), baik ada implementasi spesifik, atau Anda ingin kelas abstrak atau metode ekstensi untuk "default". Namun, dalam C #, kecuali basis kode Anda sudah menghapusI
-prefix, simpan. Buat catatan mental setiap kali Anda melihat kodeclass Something: ISomething
- artinya seseorang tidak pandai mengikuti YAGNI dan membangun abstraksi yang masuk akal.[1] - Secara teknis, ini tidak secara khusus disebutkan dalam makalah Liskov, tetapi ini adalah salah satu dasar dari kertas OOP asli dan dalam pembacaan saya tentang Liskov, dia tidak menentang ini. Dalam interpretasi yang kurang ketat (yang diambil oleh sebagian besar bahasa OOP), ini berarti bahwa setiap kode yang menggunakan tipe publik yang dimaksudkan untuk substitusi (yaitu non-final / disegel) harus bekerja dengan implementasi yang sesuai dari tipe tersebut.
sumber
Dalam dunia yang ideal, Anda tidak harus mengawali nama Anda dengan tulisan tangan pendek ("Saya ...") tetapi cukup biarkan nama tersebut mengekspresikan sebuah konsep.
Saya membaca di suatu tempat (dan tidak bisa sumbernya) pendapat bahwa konvensi ISomething ini menuntun Anda untuk mendefinisikan konsep "IDollar" ketika Anda membutuhkan kelas "Dolar", tetapi solusi yang benar adalah konsep yang disebut hanya "Mata Uang".
Dengan kata lain, konvensi ini memberikan jalan keluar yang mudah untuk kesulitan menyebutkan nama dan yang mudah secara halus salah .
Di dunia nyata, klien kode Anda (yang mungkin hanya "Anda dari masa depan") harus dapat membacanya nanti dan terbiasa dengannya secepat mungkin (atau dengan sedikit usaha) mungkin (berikan klien kode Anda apa yang mereka harapkan dapatkan).
Konvensi ISomething, memperkenalkan nama cruft / bad. Yang mengatakan, cruft bukan cruft nyata *), kecuali konvensi dan praktik yang digunakan dalam penulisan kode tidak lagi aktual; jika konvensi mengatakan "gunakan sesuatu", maka yang terbaik adalah menggunakannya, untuk menyesuaikan (dan bekerja lebih baik) dengan pengembang lainnya.
*) Saya bisa lolos dengan mengatakan ini karena "cruft" bukan konsep yang pasti :)
sumber
Seperti jawaban lain menyebutkan, awalan nama antarmuka Anda dengan
I
adalah bagian dari pedoman pengkodean untuk .NET framework. Karena kelas konkret lebih sering berinteraksi dengan daripada antarmuka generik dalam C # dan VB.NET, mereka adalah kelas pertama dalam skema penamaan dan dengan demikian harus memiliki nama paling sederhana - karenanya,IParser
untuk antarmuka danParser
untuk implementasi default.Tetapi dalam kasus Java dan bahasa yang serupa, di mana antarmuka lebih disukai daripada kelas konkret, Paman Bob benar bahwa
I
harus dihapus dan antarmuka harus memiliki nama yang paling sederhana -Parser
untuk antarmuka parser Anda, misalnya. Tetapi alih-alih nama berbasis peran sepertiParserDefault
, kelas harus memiliki nama yang menjelaskan bagaimana parser diimplementasikan :Hal ini, misalnya, bagaimana
Set<T>
,HashSet<T>
danTreeSet<T>
diberi nama.sumber
sealed
default, dan Anda harus membuatnya secara eksplisitvirtual
. Menjadi virtual adalah kasus khusus dalam C #. Tentu saja, karena pendekatan yang direkomendasikan untuk menulis kode berubah selama bertahun-tahun, banyak peninggalan masa lalu yang diperlukan untuk tetap kompatibel :) Poin utamanya adalah jika Anda mendapatkan antarmuka dalam C # (yang dimulai denganI
), Anda tahu itu sepenuhnya tipe abstrak; jika Anda mendapatkan non-antarmuka, asumsi tipikal adalah tipe yang Anda inginkan, LSP terkutuk.Anda benar karena konvensi antarmuka I = antarmuka ini melanggar aturan (baru)
Di masa lalu Anda akan mengawali semuanya dengan jenisnya intCounter boolFlag dll.
Jika Anda ingin memberi nama sesuatu tanpa awalan, tetapi juga menghindari 'ConcreteParser' Anda bisa menggunakan ruang nama
sumber
intCounter
atauboolFlag
. Itu adalah "tipe" yang salah. Sebagai gantinya, Anda akan menulispszName
(penunjuk ke string yang diakhiri NUL yang berisi nama),cchName
(jumlah karakter dalam string "nama"),xControl
(koordinat x dari kontrol),fVisible
(bendera yang menunjukkan sesuatu terlihat),pFile
(penunjuk ke file),hWindow
(pegangan ke jendela), dan sebagainya.xControl
kecchName
. Mereka berdua tipeint
, tetapi mereka memiliki semantik yang sangat berbeda. Awalan membuat semantik ini jelas bagi manusia. Pointer ke string yang diakhiri NUL terlihat persis seperti pointer ke string gaya Pascal ke kompiler. Demikian pula,I
awalan untuk antarmuka muncul dalam bahasa C ++ di mana antarmuka benar-benar hanya kelas (dan memang untuk C, di mana tidak ada yang namanya kelas atau antarmuka di tingkat bahasa, itu semua hanya sebuah konvensi).