Saya memiliki kelas yang dipanggil Heading
yang melakukan beberapa hal, tetapi seharusnya juga mampu mengembalikan kebalikan dari nilai heading saat ini, yang akhirnya harus digunakan melalui pembuatan instance baru dari Heading
kelas itu sendiri.
Saya dapat memiliki properti sederhana yang dipanggil reciprocal
untuk mengembalikan judul yang berlawanan dari nilai saat ini dan kemudian secara manual membuat contoh baru dari kelas Heading, atau saya dapat membuat metode seperti createReciprocalHeading()
untuk secara otomatis membuat contoh baru dari kelas Heading dan mengembalikannya ke pengguna.
Namun, salah satu kolega saya merekomendasikan saya untuk hanya membuat properti kelas yang disebut reciprocal
yang mengembalikan instance baru dari kelas itu sendiri melalui metode pengambil.
Pertanyaan saya adalah: Bukankah ini merupakan suatu anti-pola bagi sebuah properti kelas untuk berperilaku seperti itu?
Saya khususnya menemukan ini kurang intuitif karena:
- Dalam pikiran saya, properti kelas tidak boleh mengembalikan instance baru kelas, dan
- Nama properti, yaitu
reciprocal
, tidak membantu pengembang untuk sepenuhnya memahami perilakunya, tanpa mendapatkan bantuan dari IDE atau memeriksa tanda tangan pengambil.
Apakah saya terlalu ketat tentang apa yang harus dilakukan properti kelas atau apakah itu masalah yang sah? Saya selalu mencoba untuk mengelola keadaan kelas melalui bidang dan propertinya serta perilakunya melalui metodenya, dan saya gagal melihat bagaimana ini cocok dengan definisi properti kelas.
sumber
Heading
sebagai tipe yang tidak berubah danreciprocal
mengembalikan yang baruHeading
adalah "lubang keberhasilan" praktik yang baik. (dengan peringatan di kedua panggilan untukreciprocal
harus mengembalikan "hal yang sama", yaitu mereka harus lulus ujian kesetaraan.)Jawaban:
Ini tidak diketahui memiliki hal-hal seperti Salin () atau Klon () tapi ya saya pikir Anda benar untuk khawatir tentang yang satu ini.
ambil contoh:
Akan menyenangkan jika ada peringatan bahwa properti itu adalah objek baru setiap kali.
Anda mungkin juga berasumsi bahwa:
Namun. Jika kelas heading Anda adalah tipe nilai yang tidak dapat diubah maka Anda akan dapat menerapkan hubungan ini dan timbal balik mungkin nama yang baik untuk operasi
sumber
Copy()
danClone()
merupakan metode, bukan properti. Saya percaya OP mengacu pada pengambil properti yang mengembalikan instance baru.String.Substring()
,Array.Reverse()
,Int32.GetHashCode()
, dllIf your heading class was an immutable value type
- Saya pikir Anda harus menambahkan bahwa setter properti pada objek tidak berubah harus selalu mengembalikan instance baru (atau setidaknya jika nilai baru tidak sama dengan nilai lama), karena itulah definisi objek tidak berubah. :)Antarmuka kelas mengarahkan pengguna kelas untuk membuat asumsi tentang cara kerjanya.
Jika banyak dari asumsi ini benar dan sedikit yang salah, antarmuka bagus.
Jika banyak dari asumsi ini salah dan sedikit yang benar, antarmuka adalah sampah.
Asumsi umum tentang Properties adalah memanggil fungsi get itu murah. Asumsi umum lainnya tentang properti adalah memanggil fungsi get dua kali berturut-turut akan mengembalikan hal yang sama.
Anda dapat mengatasi ini dengan menggunakan konsistensi, untuk mengubah harapan. Sebagai contoh, untuk sebuah perpustakaan 3D kecil di mana Anda perlu
Vector
,Ray
Matrix
, dll Anda dapat membuatnya seperti yang getter sepertiVector.normal
danMatrix.inverse
cukup banyak selalu mahal. Sayangnya bahkan jika antarmuka Anda secara konsisten menggunakan properti yang mahal, antarmuka yang terbaik akan seintuitif yang menggunakanVector.create_normal()
danMatrix.create_inverse()
- namun saya tahu tidak ada argumen kuat yang dapat dibuat bahwa menggunakan properti menciptakan antarmuka yang lebih intuitif, bahkan setelah mengubah harapan.sumber
Properti harus kembali dengan sangat cepat, mengembalikan nilai yang sama dengan panggilan berulang, dan mendapatkan nilainya tidak memiliki efek samping. Apa yang Anda jelaskan di sini tidak boleh diterapkan sebagai properti.
Intuisi Anda secara luas benar. Metode pabrik tidak mengembalikan nilai yang sama dengan panggilan berulang. Ini mengembalikan contoh baru setiap kali. Ini cukup banyak mendiskualifikasi sebagai properti. Ini juga tidak cepat jika pengembangan kemudian menambah bobot pada pembuatan instance, misalnya ketergantungan jaringan.
Properti umumnya harus operasi yang sangat sederhana yang mendapatkan / mengatur bagian dari keadaan kelas saat ini. Operasi seperti pabrik tidak memenuhi kriteria ini.
sumber
DateTime.Now
properti umum digunakan dan mengacu pada objek baru. Saya pikir nama properti dapat membantu menghilangkan ambiguitas tentang apakah objek yang sama dikembalikan setiap kali atau tidak. Itu semua atas nama dan cara penggunaannya.DateTime
merupakan tipe nilai, dan tidak memiliki konsep identitas objek. Jika saya mengerti dengan benar, masalahnya adalah tidak menentukan dan mengembalikan nilai baru setiap kali.DateTime.New
adalah statis, dan karena itu Anda tidak dapat mengharapkannya untuk mewakili keadaan suatu objek.Saya tidak berpikir ada jawaban bahasa-agnostik untuk ini karena apa yang merupakan "properti" adalah pertanyaan khusus-bahasa, dan apa yang diharapkan penelepon dari "properti" adalah pertanyaan khusus bahasa. Saya pikir cara yang paling bermanfaat untuk memikirkan hal ini adalah dengan memikirkan seperti apa penampilannya dari sudut pandang si penelepon.
Dalam C #, properti berbeda karena sifatnya (secara konvensional) dikapitalisasi (seperti metode) tetapi tidak memiliki tanda kurung (seperti variabel instance publik). Jika Anda melihat kode berikut, tidak ada dokumentasi, apa yang Anda harapkan?
Sebagai pemula C #, tetapi orang yang membaca Panduan Penggunaan Properti Microsoft , saya berharap
Reciprocal
, antara lain:Heading
kelasReciprocalChanged
acaraDari asumsi-asumsi ini, (3) dan (4) mungkin benar (dengan asumsi
Heading
adalah tipe nilai yang tidak berubah, seperti dalam jawaban Ewan ), (1) dapat diperdebatkan, (2) tidak diketahui tetapi juga dapat diperdebatkan, dan (5) tidak mungkin untuk masuk akal semantik (meskipun apa pun yang memiliki tajuk mungkin harus memilikiHeadingChanged
acara). Ini menunjukkan kepada saya bahwa dalam C # API, "dapatkan atau hitung timbal balik" tidak boleh diimplementasikan sebagai properti, tetapi terutama jika perhitungannya murah danHeading
tidak dapat diubah, ini merupakan kasus batas.(Namun, perlu diketahui bahwa tidak ada satu pun dari kekhawatiran ini yang ada hubungannya dengan apakah memanggil properti menciptakan contoh baru , bahkan tidak (2). Membuat objek di CLR, dengan sendirinya, cukup murah.)
Di Jawa, properti adalah konvensi penamaan metode. Jika saya melihat
harapan saya mirip dengan yang di atas (jika tidak secara eksplisit ditetapkan): Saya berharap panggilan menjadi murah, idempoten, dan kurang dalam efek samping. Namun, di luar kerangka JavaBeans konsep "properti" tidak terlalu berarti di Jawa, dan terutama ketika mempertimbangkan properti yang tidak dapat diubah tanpa korespondensi
setReciprocal()
,getXXX()
konvensi ini sekarang agak kuno. Dari Effective Java , edisi kedua (sudah lebih dari delapan tahun sekarang):Dalam API kontemporer dan lebih lancar, saya berharap bisa melihatnya
- yang sekali lagi akan menyarankan bahwa panggilan itu murah, idempoten, dan tidak memiliki efek samping, tetapi tidak akan mengatakan apa-apa tentang apakah perhitungan baru dilakukan atau objek baru dibuat. Ini baik; dalam API yang baik, saya seharusnya tidak peduli.
Di Ruby, tidak ada yang namanya properti. Ada "atribut", tetapi jika saya melihat
Saya tidak memiliki cara langsung untuk mengetahui apakah saya mengakses variabel instan
@reciprocal
melaluiattr_reader
metode accessor sederhana, atau apakah saya memanggil metode yang melakukan perhitungan mahal. Fakta bahwa nama metode adalah kata benda sederhana, meskipun, daripada mengatakancalcReciprocal
, menyarankan, sekali lagi, bahwa panggilan itu setidaknya murah dan mungkin tidak memiliki efek samping.Dalam Scala, konvensi penamaan adalah bahwa metode dengan efek samping mengambil kurung dan metode tanpa mereka tidak, tetapi
dapat berupa:
(Perhatikan bahwa Scala memungkinkan berbagai hal yang tetap tidak disarankan oleh panduan gaya . Ini adalah salah satu gangguan utama saya pada Scala.)
Kurangnya tanda kurung memberi tahu saya bahwa panggilan itu tidak memiliki efek samping; namanya, sekali lagi, menunjukkan bahwa panggilan itu harus relatif murah. Di luar itu, saya tidak peduli bagaimana hal itu memberi saya nilai.
Singkatnya: Ketahui bahasa yang Anda gunakan, dan tahu apa harapan yang akan dibawa oleh programmer lain ke API Anda. Yang lainnya adalah detail implementasi.
sumber
Seperti yang orang lain katakan, itu adalah pola yang agak umum untuk mengembalikan instance dari kelas yang sama.
Penamaan harus berjalan seiring dengan konvensi penamaan bahasa.
Sebagai contoh, di Jawa saya mungkin berharap akan dipanggil
getReciprocal();
Yang sedang berkata, saya akan mempertimbangkan benda yang bisa berubah vs benda yang tidak berubah .
Dengan yang tidak berubah , banyak hal yang mudah, dan tidak ada salahnya jika Anda mengembalikan objek yang sama atau tidak.
Dengan yang bisa berubah , ini bisa menjadi sangat menakutkan.
Sekarang apa yang dimaksud dengan b? Kebalikan dari nilai asli a? Atau kebalikan dari yang berubah? Ini mungkin contoh yang terlalu pendek, tetapi Anda mengerti maksudnya.
Dalam kasus tersebut cari penamaan yang lebih baik yang mengkomunikasikan apa yang terjadi, misalnya
createReciprocal()
mungkin merupakan pilihan yang lebih baik.Tapi itu benar-benar tergantung pada konteksnya juga.
sumber
getReciprocal()
, karena itu "terdengar" seperti gaya pengambil JavaBeans "normal". Lebih suka "createXXX ()" atau mungkin "calculXXX ()" yang mengindikasikan atau memberi petunjuk kepada programmer lain bahwa sesuatu yang berbeda sedang terjadiUntuk kepentingan single-responsiblity dan kejelasan saya akan memiliki ReverseHeadingFactory yang mengambil objek dan mengembalikannya. Ini akan membuatnya lebih jelas bahwa objek baru sedang dikembalikan dan akan berarti kode untuk menghasilkan kebalikannya adalah enkapsulasi dari kode lain.
sumber
Tergantung. Saya biasanya mengharapkan properti mengembalikan hal-hal yang merupakan bagian dari instance, jadi mengembalikan instance berbeda dari kelas yang sama akan sedikit tidak biasa.
Tetapi misalnya kelas string dapat memiliki properti "firstWord", "lastWord", atau jika ia menangani Unicode "firstLetter", "lastLetter" yang akan menjadi objek string penuh - biasanya hanya yang lebih kecil.
sumber
Karena jawaban lain mencakup bagian Properti, saya hanya akan menjawab # 2 tentang
reciprocal
:Jangan gunakan apa pun selain
reciprocal
. Ini adalah satu-satunya istilah yang tepat untuk apa yang Anda gambarkan (dan itu adalah istilah formal). Jangan menulis perangkat lunak yang salah untuk menyelamatkan pengembang Anda dari mempelajari domain tempat mereka bekerja. Dalam navigasi, istilah memiliki makna yang sangat spesifik dan menggunakan sesuatu yang tampaknya tidak berbahayareverse
atauopposite
dapat menyebabkan kebingungan dalam konteks tertentu.sumber
Secara logis, tidak, ini bukan properti dari heading. Jika ya, Anda juga bisa mengatakan bahwa negatif dari angka adalah properti dari angka itu, dan terus terang yang akan membuat "properti" kehilangan semua makna, hampir semuanya akan menjadi properti.
Timbal balik adalah fungsi murni dari sebuah pos, sama seperti negatif dari suatu angka adalah fungsi murni dari nomor tersebut.
Sebagai aturan praktis, jika pengaturannya tidak masuk akal, itu mungkin bukan properti. Pengaturan itu masih dapat dianulir tentu saja, tetapi menambahkan setter harus secara teori memungkinkan dan bermakna.
Sekarang, dalam beberapa bahasa, mungkin masuk akal untuk memilikinya sebagai properti sebagaimana ditentukan dalam konteks bahasa itu, jika itu membawa beberapa keuntungan karena mekanisme bahasa itu. Misalnya, jika Anda ingin menyimpannya ke database, mungkin masuk akal untuk menjadikannya sebagai properti jika memungkinkan penanganan otomatis oleh kerangka kerja ORM. Atau mungkin itu adalah bahasa di mana tidak ada perbedaan antara properti dan fungsi anggota tanpa parameter (saya tidak tahu bahasa seperti itu, tapi saya yakin ada beberapa). Kemudian tergantung pada perancang API dan dokumenter untuk membedakan antara fungsi dan properti.
Sunting: Khusus untuk C #, saya akan melihat kelas yang ada, terutama yang ada di Perpustakaan Standar.
BigInteger
danComplex
struktur. Tetapi mereka adalah struktur, dan hanya memiliki fungsi statis, dan semua properti memiliki tipe yang berbeda. Jadi tidak banyak desain yang membantu untukHeading
kelas Anda .Vector
kelas , yang memiliki sedikit properti, dan tampaknya cukup dekat dengan AndaHeading
. Jika Anda melakukannya, maka Anda akan memiliki fungsi untuk timbal balik, bukan properti.Anda mungkin ingin melihat perpustakaan yang benar-benar digunakan oleh kode Anda, atau kelas serupa yang ada. Cobalah untuk tetap konsisten, itu biasanya yang terbaik, jika satu cara tidak jelas benar dan cara lain salah.
sumber
Pertanyaan yang sangat menarik. Saya melihat tidak ada pelanggaran SOLID + KERING + CIUMAN dalam apa yang Anda usulkan, tapi tetap saja baunya tidak enak.
Metode yang mengembalikan instance kelas disebut konstruktor , kan? jadi Anda membuat contoh baru dari metode non-konstruktor. Ini bukan akhir dunia, tetapi sebagai klien, saya tidak akan mengharapkan itu. Ini biasanya dilakukan dalam keadaan tertentu: pabrik atau, kadang-kadang, metode statis dari kelas yang sama (berguna untuk lajang dan untuk ... programmer yang tidak terlalu berorientasi objek?)
Plus: apa yang terjadi jika Anda memohon
getReciprocal()
pada objek yang dikembalikan? Anda mendapatkan instance kelas yang lain, itu bisa jadi salinan yang tepat dari yang pertama! Dan jika Anda memohongetReciprocal()
pada satu itu? Obyek siapa saja yang tergeletak?Sekali lagi: bagaimana jika Invoker yang membutuhkan timbal balik nilai (sebagai skalar, maksudku)?
getReciprocal()->getValue()
? kami melanggar Hukum Demeter untuk keuntungan kecil.sumber