Saya suka "pola" abadi karena kekuatannya, dan di masa lalu saya merasa bermanfaat untuk merancang sistem dengan tipe data yang tidak dapat diubah (beberapa, sebagian atau bahkan semua). Seringkali ketika saya melakukannya, saya menemukan diri saya menulis lebih sedikit bug dan men-debug jauh lebih mudah.
Namun, teman-teman saya pada umumnya menghindar dari kekekalan. Mereka sama sekali tidak berpengalaman (jauh dari itu), namun mereka menulis objek data dengan cara klasik - anggota pribadi dengan pengambil dan penyetel untuk setiap anggota. Maka biasanya konstruktor mereka tidak mengambil argumen, atau mungkin hanya mengambil beberapa argumen untuk kenyamanan. Begitu sering, membuat objek terlihat seperti ini:
Foo a = new Foo();
a.setProperty1("asdf");
a.setProperty2("bcde");
Mungkin mereka melakukan itu di mana-mana. Mungkin mereka bahkan tidak mendefinisikan konstruktor yang mengambil dua string, tidak peduli betapa pentingnya mereka. Dan mungkin mereka tidak mengubah nilai string itu nanti dan tidak perlu. Jelas jika hal-hal itu benar, objek akan dirancang lebih baik sebagai abadi, bukan? (konstruktor mengambil dua properti, tidak ada setter sama sekali).
Bagaimana Anda memutuskan apakah suatu jenis objek harus dirancang sebagai tidak berubah? Apakah ada seperangkat kriteria yang baik untuk menilai itu?
Saat ini saya sedang berdebat apakah akan mengganti beberapa tipe data dalam proyek saya sendiri menjadi tidak berubah, tetapi saya harus membenarkannya kepada rekan-rekan saya, dan data dalam tipe tersebut mungkin (SANGAT jarang) berubah - pada saat itu tentu saja Anda dapat mengubah itu cara abadi (buat yang baru, salin properti dari objek lama kecuali yang ingin Anda ubah). Tetapi saya tidak yakin apakah ini hanya kecintaan saya terhadap kekekalan yang ditunjukkan, atau apakah ada kebutuhan aktual / manfaat dari mereka.
sumber
Jawaban:
Sepertinya Anda mendekatinya ke belakang. Orang harus default ke tidak berubah. Hanya membuat objek bisa berubah jika Anda benar-benar harus / tidak bisa membuatnya berfungsi sebagai objek abadi.
sumber
Manfaat utama benda tidak bergerak adalah menjamin keamanan benang. Di dunia di mana banyak inti dan utas adalah norma, manfaat ini menjadi sangat penting.
Tetapi menggunakan benda yang bisa berubah sangat nyaman. Mereka berkinerja baik, dan selama Anda tidak memodifikasi mereka dari utas terpisah, dan Anda memiliki pemahaman yang baik tentang apa yang Anda lakukan, mereka cukup dapat diandalkan.
sumber
Ada dua cara utama untuk memutuskan apakah suatu objek tidak dapat diubah.
a) Berdasarkan sifat Objek
Mudah untuk menangkap situasi ini karena kita tahu bahwa benda-benda ini tidak akan berubah setelah dibangun. Misalnya jika Anda memiliki
RequestHistory
entitas dan entitas sejarah alami tidak berubah setelah dibangun. Objek-objek ini dapat langsung dirancang sebagai kelas yang tidak berubah. Perlu diingat bahwa Obyek Permintaan adalah berkualitas karena dapat mengubah keadaannya dan kepada siapa ia ditugaskan ke dll dari waktu ke waktu tetapi sejarah permintaan tidak berubah. Misalnya, ada elemen riwayat yang dibuat minggu lalu ketika dipindahkan dari status diserahkan ke yang ditugaskan DAN entitiy sejarah ini tidak pernah bisa berubah. Jadi ini adalah kasus abadi klasik.b) Berdasarkan pilihan desain, faktor eksternal
Ini mirip dengan contoh java.lang.String. String sebenarnya dapat berubah dari waktu ke waktu tetapi dengan desain, mereka menjadikannya tidak dapat diubah karena faktor caching / string pool / concurrency. Sama halnya caching / konkurensi, dll. Dapat memainkan peran yang baik dalam membuat objek tidak bergerak jika caching / konkurensi dan kinerja terkait sangat penting dalam aplikasi. Tetapi keputusan ini harus diambil dengan sangat hati-hati setelah menganalisis semua dampaknya.
Keuntungan utama dari objek yang tidak dapat diubah adalah mereka tidak mengalami pola guling-guling. Yaitu objek tidak akan menerima perubahan apa pun selama masa hidup dan itu membuat pengkodean dan pemeliharaannya sangat sangat mudah.
sumber
Meminimalkan keadaan suatu program sangat bermanfaat.
Tanyakan kepada mereka apakah mereka ingin menggunakan tipe nilai yang bisa berubah di salah satu kelas Anda untuk penyimpanan sementara dari kelas klien.
Jika mereka menjawab ya, tanyakan mengapa? Keadaan yang tidak dapat berubah tidak termasuk dalam skenario seperti ini. Memaksa mereka untuk membuat negara di mana ia sebenarnya berada dan menjadikan status tipe data Anda sejelas mungkin adalah alasan yang sangat baik.
sumber
Jawabannya agak tergantung pada bahasa. Kode Anda terlihat seperti Java, di mana masalah ini sesulit mungkin. Di Jawa, objek hanya bisa dilewatkan dengan referensi, dan klon benar-benar rusak.
Tidak ada jawaban sederhana, tetapi yang pasti Anda ingin membuat objek nilai ish kecil tidak dapat diubah. Java dengan benar membuat String tidak dapat diubah, tetapi salah membuat Tanggal dan Kalender bisa berubah.
Jadi pasti membuat objek bernilai kecil tidak berubah, dan mengimplementasikan copy constructor. Lupakan semua tentang Cloneable, itu dirancang sangat buruk sehingga tidak berguna.
Untuk objek bernilai lebih besar, jika tidak nyaman untuk membuatnya tidak berubah, maka membuatnya mudah untuk disalin.
sumber
Agak kontra-intuitif, tidak perlu mengubah string di kemudian hari adalah argumen yang cukup bagus bahwa tidak masalah apakah objek tidak berubah atau tidak. Programmer sudah memperlakukan mereka sebagai tidak dapat diubah secara efektif apakah kompiler memaksakannya atau tidak.
Kekekalan biasanya tidak menyakitkan, tetapi juga tidak selalu membantu. Cara mudah untuk mengetahui apakah objek Anda mungkin mendapat manfaat dari ketidakberubahan adalah jika Anda perlu membuat salinan objek atau mendapatkan mutex sebelum mengubahnya . Jika tidak pernah berubah, maka ketidakmampuan tidak benar-benar membelikan Anda apa-apa, dan terkadang membuat segalanya lebih rumit.
Anda memang memiliki poin yang baik tentang risiko membangun objek dalam keadaan tidak valid, tapi itu benar-benar masalah terpisah dari ketidakberubahan. Objek dapat berubah - ubah dan selalu dalam keadaan valid setelah konstruksi.
Pengecualian untuk aturan itu adalah karena Java tidak mendukung parameter bernama atau parameter default, kadang-kadang bisa sulit untuk mendesain kelas yang menjamin objek yang valid hanya menggunakan konstruktor kelebihan beban. Tidak begitu banyak dengan kasus dua-properti, tetapi kemudian ada sesuatu yang bisa dikatakan untuk konsistensi juga, jika pola itu sering terjadi dengan kelas yang serupa tetapi lebih besar di bagian lain dari kode Anda.
sumber
Object
fitur tersembunyi" -nya , kemudian mengubah suatu objek secara langsung tidak ada bedanya dengan menimpa referensi dengan referensi ke objek baru yang sebelumnya identik tetapi untuk perubahan yang ditunjukkan. Salah satu kelemahan semantik terbesar di Jawa, IMHO, adalah bahwa ia tidak memiliki cara kode yang dapat menunjukkan bahwa variabel hanya satu-satunya referensi non-sesaat untuk sesuatu; referensi hanya boleh diloloskan ke metode yang tidak mungkin menyimpan salinan setelah mereka kembali.Saya mungkin memiliki pandangan tingkat terlalu rendah tentang hal ini dan kemungkinan karena saya menggunakan C dan C ++ yang tidak persis membuatnya begitu mudah untuk membuat semuanya tidak berubah, tapi saya melihat tipe data yang tidak dapat diubah sebagai detail optimasi untuk menulis lebih banyak fungsi efisien tanpa efek samping dan dapat dengan mudah memberikan fitur seperti sistem undo dan pengeditan non-destruktif.
Misalnya, ini bisa sangat mahal:
... jika
Mesh
tidak dirancang untuk menjadi struktur data yang persisten dan, sebaliknya, merupakan tipe data yang harus disalin secara penuh (yang dapat menjangkau gigabyte dalam beberapa skenario) bahkan jika semua yang akan kita lakukan adalah mengubah sebuah bagian dari itu (seperti dalam skenario di atas di mana kami hanya mengubah posisi titik).Jadi saat itulah saya meraih kekekalan dan mendesain struktur data untuk memungkinkan bagian yang tidak dimodifikasi itu disalin dangkal dan referensi dihitung, untuk memungkinkan fungsi di atas menjadi cukup efisien tanpa harus menyalin seluruh jerat sekitar sementara masih bisa menulis berfungsi untuk bebas dari efek samping yang secara dramatis menyederhanakan keamanan benang, keselamatan pengecualian, kemampuan untuk membatalkan operasi, menerapkannya tanpa merusak, dll.
Dalam kasus saya, terlalu mahal (setidaknya dari sudut pandang produktivitas) untuk membuat semuanya tidak berubah, jadi saya menyimpannya untuk kelas yang terlalu mahal untuk menyalin sepenuhnya. Kelas-kelas itu biasanya struktur data yang kuat seperti jerat dan gambar dan saya biasanya menggunakan antarmuka yang bisa berubah untuk mengekspresikan perubahan kepada mereka melalui objek "pembangun" untuk mendapatkan salinan baru yang tidak dapat diubah. Dan saya tidak melakukan itu terlalu banyak untuk mencoba mencapai jaminan abadi di tingkat pusat kelas begitu banyak membantu saya untuk menggunakan kelas dalam fungsi yang dapat bebas dari efek samping. Keinginan saya untuk membuat
Mesh
abadi di atas tidak secara langsung membuat jerat tidak dapat diubah, tetapi untuk memungkinkan dengan mudah menulis fungsi bebas dari efek samping yang menginputkan mesh dan menghasilkan yang baru tanpa membayar memori besar dan biaya komputasi sebagai imbalan.Sebagai hasilnya, saya hanya memiliki 4 tipe data yang tidak dapat diubah di seluruh basis kode saya, dan mereka semua struktur data yang besar, tetapi saya sangat menggunakannya untuk membantu saya menulis fungsi yang bebas dari efek samping. Jawaban ini mungkin berlaku jika, seperti saya, Anda bekerja dalam bahasa yang tidak membuatnya begitu mudah untuk membuat semuanya tidak berubah. Dalam hal ini, Anda dapat mengalihkan fokus Anda untuk membuat sebagian besar fungsi Anda menghindari efek samping, pada titik mana Anda mungkin ingin membuat struktur data terpilih tidak berubah, tipe PDS, sebagai detail optimisasi untuk menghindari salinan lengkap yang mahal. Sementara itu ketika saya memiliki fungsi seperti ini:
... maka saya tidak memiliki godaan untuk membuat vektor tidak dapat diubah karena mereka cukup murah untuk hanya menyalin secara penuh. Tidak ada keuntungan kinerja yang diperoleh di sini dengan mengubah Vektor menjadi struktur yang tidak dapat diubah yang dapat menyalin bagian yang tidak dimodifikasi dengan dangkal. Biaya seperti itu akan lebih besar daripada biaya hanya menyalin seluruh vektor.
sumber
Jika Foo hanya memiliki dua properti, maka mudah untuk menulis konstruktor yang membutuhkan dua argumen. Tapi misalkan Anda menambahkan properti lain. Maka Anda harus menambahkan konstruktor lain:
Pada titik ini, masih dapat dikelola. Tetapi bagaimana jika ada 10 properti? Anda tidak ingin konstruktor dengan 10 argumen. Anda bisa menggunakan pola builder untuk membuat instance, tetapi itu menambah kompleksitas. Pada saat ini, Anda akan berpikir "mengapa saya tidak menambahkan setter untuk semua properti seperti yang dilakukan orang normal"?
sumber