Saya mencoba untuk membuat kepala saya berputar-putar vs benda yang tidak bisa diubah. Menggunakan objek yang bisa berubah mendapat banyak tekanan buruk (misalnya mengembalikan serangkaian string dari suatu metode) tapi saya mengalami kesulitan memahami apa dampak negatif dari ini. Apa praktik terbaik di sekitar menggunakan objek yang bisa berubah? Haruskah Anda menghindarinya sedapat mungkin?
oop
immutability
mutable
Alex Angas
sumber
sumber
string
tidak berubah, setidaknya dalam. NET, dan saya pikir dalam banyak bahasa modern lainnya juga.Jawaban:
Nah, ada beberapa aspek dalam hal ini.
Objek yang bisa berubah tanpa referensi-identitas dapat menyebabkan bug di waktu ganjil. Misalnya, pertimbangkan
Person
kacang denganequals
metode berbasis nilai :Mesin
Person
virtual akan "hilang" di peta ketika digunakan sebagai kunci karena sifatnyahashCode
dan kesetaraannya didasarkan pada nilai yang bisa diubah. Nilai-nilai itu berubah di luar peta dan semua hashing menjadi usang. Para ahli teori suka mengomel tentang hal ini, tetapi dalam praktiknya saya belum menganggapnya sebagai masalah yang terlalu besar.Aspek lain adalah "kewajaran" logis dari kode Anda. Ini adalah istilah yang sulit untuk didefinisikan, mencakup segalanya mulai dari keterbacaan hingga mengalir. Secara umum, Anda harus dapat melihat sepotong kode dan dengan mudah memahami apa yang dilakukannya. Tetapi yang lebih penting dari itu, Anda harus bisa meyakinkan diri sendiri bahwa itu melakukan apa yang dilakukannya dengan benar . Ketika objek dapat berubah secara independen di berbagai kode "domain", terkadang menjadi sulit untuk melacak apa yang ada di mana dan mengapa (" aksi seram di kejauhan "). Ini adalah konsep yang lebih sulit untuk dicontohkan, tetapi ini adalah sesuatu yang sering dihadapi dalam arsitektur yang lebih besar dan lebih kompleks.
Akhirnya, objek yang bisa berubah adalah pembunuh dalam situasi bersamaan. Setiap kali Anda mengakses objek yang bisa berubah dari utas terpisah, Anda harus berurusan dengan penguncian. Ini mengurangi throughput dan membuat kode Anda secara dramatis lebih sulit untuk dipelihara. Suatu sistem yang cukup rumit meniup masalah ini sejauh tidak proporsional sehingga menjadi hampir tidak mungkin untuk dipertahankan (bahkan untuk para ahli konkurensi).
Objek yang tidak dapat diubah (dan lebih khusus lagi, koleksi yang tidak dapat diubah) menghindari semua masalah ini. Setelah Anda memikirkan bagaimana cara kerjanya, kode Anda akan berkembang menjadi sesuatu yang lebih mudah dibaca, lebih mudah untuk dipelihara dan kecil kemungkinannya untuk gagal dengan cara yang aneh dan tidak terduga. Objek yang tidak dapat diubah bahkan lebih mudah untuk diuji, karena tidak hanya sifatnya yang mudah dipermainkan, tetapi juga pola kode yang cenderung ditegakkan. Singkatnya, mereka latihan yang baik di sekitar!
Dengan itu, saya hampir tidak fanatik dalam hal ini. Beberapa masalah tidak bisa dimodelkan dengan baik ketika semuanya tidak berubah. Tapi saya pikir Anda harus mencoba untuk mendorong sebanyak mungkin kode Anda ke arah itu, dengan asumsi tentu saja bahwa Anda menggunakan bahasa yang membuat ini pendapat yang dapat dipertahankan (C / C ++ membuat ini sangat sulit, seperti halnya Java) . Singkatnya: keuntungannya agak tergantung pada masalah Anda, tetapi saya cenderung lebih memilih kekekalan.
sumber
Objek yang Tidak Berubah vs Koleksi yang Tidak Berubah
Salah satu poin penting dalam debat tentang objek yang dapat berubah dan tidak berubah adalah kemungkinan memperluas konsep keabadian ke koleksi. Objek yang tidak dapat diubah adalah objek yang sering mewakili struktur logis data tunggal (misalnya string yang tidak dapat diubah). Ketika Anda memiliki referensi ke objek abadi, konten objek tidak akan berubah.
Koleksi abadi adalah koleksi yang tidak pernah berubah.
Ketika saya melakukan operasi pada koleksi yang bisa diubah, maka saya mengubah koleksi di tempat, dan semua entitas yang memiliki referensi ke koleksi akan melihat perubahan.
Ketika saya melakukan operasi pada koleksi yang tidak dapat diubah, referensi dikembalikan ke koleksi baru yang mencerminkan perubahan. Semua entitas yang memiliki referensi ke versi koleksi sebelumnya tidak akan melihat perubahan.
Implementasi yang cerdik tidak perlu menyalin (mengkloning) seluruh koleksi untuk memberikan keabadian itu. Contoh paling sederhana adalah stack diimplementasikan sebagai daftar yang terhubung secara tunggal dan operasi push / pop. Anda dapat menggunakan kembali semua node dari koleksi sebelumnya di koleksi baru, menambahkan hanya satu node untuk push, dan kloning tidak ada node untuk pop. Operasi push_tail pada daftar yang terhubung sendiri, di sisi lain, tidak begitu sederhana atau efisien.
Variabel / referensi tidak berubah vs dapat diubah
Beberapa bahasa fungsional mengambil konsep immutability ke objek referensi sendiri, hanya memungkinkan penugasan referensi tunggal.
Kemudahan Pengembangan vs. Kinerja
Hampir selalu alasan untuk menggunakan objek yang tidak dapat diubah adalah untuk mempromosikan pemrograman bebas efek samping dan alasan sederhana tentang kode (terutama dalam lingkungan paralel / sangat bersamaan). Anda tidak perlu khawatir tentang data dasar yang diubah oleh entitas lain jika objek tidak berubah.
Kelemahan utama adalah kinerja. Berikut ini adalah tulisan pada tes sederhana yang saya lakukan di Jawa membandingkan beberapa objek yang tidak berubah vs bisa berubah dalam masalah mainan.
Masalah kinerja diperdebatkan di banyak aplikasi, tetapi tidak semua, itulah sebabnya banyak paket numerik besar, seperti kelas Numpy Array di Python, memungkinkan pembaruan In-Place dari array besar. Ini akan menjadi penting untuk area aplikasi yang memanfaatkan operasi matriks dan vektor yang besar. Masalah paralel-data dan intensif komputasi yang besar ini mencapai kecepatan tinggi dengan beroperasi di tempat.
sumber
Periksa posting blog ini: http://www.yegor256.com/2014/06/09/objects-should-be-immutable.html . Ini menjelaskan mengapa objek tidak berubah lebih baik daripada bisa berubah. Pendeknya:
sumber
Benda yang tidak dapat berubah adalah konsep yang sangat kuat. Mereka menghilangkan banyak beban untuk mencoba menjaga objek / variabel tetap konsisten untuk semua klien.
Anda bisa menggunakannya untuk objek non-polimorfik tingkat rendah - seperti kelas CPoint - yang sebagian besar digunakan dengan semantik nilai.
Atau Anda dapat menggunakannya untuk level tinggi, antarmuka polimorfik - seperti IFunction yang mewakili fungsi matematika - yang digunakan secara khusus dengan semantik objek.
Keuntungan terbesar: imutabilitas + semantik objek + pointer pintar menjadikan kepemilikan objek tidak menjadi masalah, semua klien objek memiliki salinan pribadi mereka secara default. Secara implisit ini juga berarti perilaku deterministik di hadapan konkurensi.
Kerugian: ketika digunakan dengan objek yang berisi banyak data, konsumsi memori dapat menjadi masalah. Solusi untuk ini bisa dengan menjaga operasi pada objek simbolis dan melakukan evaluasi malas. Namun, ini kemudian dapat mengarah pada rantai perhitungan simbolik, yang dapat memengaruhi kinerja secara negatif jika antarmuka tidak dirancang untuk mengakomodasi operasi simbolik. Sesuatu yang pasti harus dihindari dalam hal ini adalah mengembalikan potongan memori besar dari suatu metode. Dalam kombinasi dengan operasi simbolik berantai, ini dapat menyebabkan konsumsi memori yang besar dan penurunan kinerja.
Jadi objek yang tidak berubah jelas merupakan cara utama saya untuk berpikir tentang desain berorientasi objek, tetapi mereka bukan dogma. Mereka memecahkan banyak masalah untuk klien objek, tetapi juga membuat banyak, terutama untuk para pelaksana.
sumber
Anda harus menentukan bahasa apa yang Anda bicarakan. Untuk bahasa tingkat rendah seperti C atau C ++, saya lebih suka menggunakan objek yang bisa berubah untuk menghemat ruang dan mengurangi churn memori. Dalam bahasa tingkat yang lebih tinggi, objek yang tidak dapat diubah membuatnya lebih mudah untuk mempertimbangkan perilaku kode (terutama kode multi-utas) karena tidak ada "aksi seram di kejauhan".
sumber
Objek yang bisa berubah hanyalah sebuah objek yang dapat dimodifikasi setelah dibuat / dipakai, vs objek yang tidak dapat diubah yang tidak dapat dimodifikasi (lihat halaman Wikipedia pada subjek). Contoh dari ini dalam bahasa pemrograman adalah daftar dan tupel Python. Daftar dapat dimodifikasi (misalnya, item baru dapat ditambahkan setelah dibuat) sedangkan tupel tidak bisa.
Saya tidak benar-benar berpikir ada jawaban yang jelas mengenai mana yang lebih baik untuk semua situasi. Mereka berdua memiliki tempat masing-masing.
sumber
Jika tipe kelas bisa berubah, variabel dari tipe kelas itu dapat memiliki sejumlah arti yang berbeda. Sebagai contoh, anggaplah suatu objek
foo
memiliki bidangint[] arr
, dan itu memegang referensi keint[3]
holding angka {5, 7, 9}. Meskipun jenis bidangnya diketahui, setidaknya ada empat hal berbeda yang dapat diwakilinya:Referensi yang berpotensi dibagikan, semua pemiliknya hanya peduli bahwa itu merangkum nilai 5, 7, dan 9. Jika
foo
inginarr
merangkum nilai yang berbeda, ia harus menggantinya dengan array berbeda yang berisi nilai yang diinginkan. Jika seseorang ingin membuat salinanfoo
, seseorang dapat memberikan salinan referensiarr
atau array baru yang menyimpan nilai {1,2,3}, mana yang lebih nyaman.Satu-satunya referensi, di mana saja di alam semesta, ke array yang merangkum nilai 5, 7, dan 9. set dari tiga lokasi penyimpanan yang saat ini memegang nilai 5, 7, dan 9; jika
foo
ingin mengenkapsulasi nilai 5, 8, dan 9, itu dapat mengubah item kedua dalam array itu atau membuat array baru yang menyimpan nilai 5, 8, dan 9 dan meninggalkan yang lama. Perhatikan bahwa jika seseorang ingin membuat salinanfoo
, orang harus menyalin menggantiarr
dengan referensi ke array baru agarfoo.arr
tetap sebagai satu-satunya referensi ke array itu di mana saja di alam semesta.Referensi ke array yang dimiliki oleh beberapa objek lain yang telah mengeksposnya
foo
untuk beberapa alasan (misalnya mungkin inginfoo
menyimpan beberapa data di sana). Dalam skenario ini,arr
tidak merangkum konten array, melainkan identitasnya . Karena menggantiarr
dengan referensi ke array baru akan benar-benar mengubah maknanya, salinanfoo
harus memiliki referensi ke array yang sama.Referensi ke array yang
foo
merupakan pemilik tunggal, tetapi referensi yang dipegang oleh objek lain untuk beberapa alasan (misalnya ia ingin memiliki objek lain untuk menyimpan data di sana - sisi sebaliknya dari kasus sebelumnya). Dalam skenario ini,arr
merangkum identitas array dan kontennya. Menggantiarr
dengan referensi ke array baru akan benar-benar mengubah maknanya, tetapi memiliki clone yangarr
mengacufoo.arr
akan melanggar asumsi bahwafoo
adalah pemilik tunggal. Dengan demikian tidak ada cara untuk menyalinfoo
.Secara teori,
int[]
harus menjadi tipe sederhana yang didefinisikan dengan baik, tetapi memiliki empat arti yang sangat berbeda. Sebaliknya, referensi ke objek yang tidak dapat diubah (mis.String
) Umumnya hanya memiliki satu makna. Sebagian besar "kekuatan" benda yang tidak dapat diubah berasal dari fakta itu.sumber
Mesin virtual yang dapat diubah dilewatkan dengan referensi.
Instance yang tidak dapat diubah diteruskan oleh nilai.
Contoh abstrak. Misalkan ada file bernama txtfile di HDD saya. Sekarang, ketika Anda meminta txtfile dari saya, saya dapat mengembalikannya dalam dua mode:
Dalam mode pertama, txtfile yang dikembalikan adalah file yang dapat diubah, karena ketika Anda melakukan perubahan pada file pintasan, Anda juga melakukan perubahan pada file asli. Kelebihan dari mode ini adalah bahwa setiap pintasan yang dikembalikan memerlukan lebih sedikit memori (pada RAM atau dalam HDD) dan kerugiannya adalah setiap orang (tidak hanya saya, pemilik) memiliki izin untuk mengubah konten file.
Dalam mode kedua, txtfile yang dikembalikan adalah file yang tidak dapat diubah, karena semua perubahan pada file yang diterima tidak merujuk ke file asli. Kelebihan dari mode ini adalah hanya saya (pemilik) yang dapat memodifikasi file asli dan kelemahannya adalah setiap salinan yang dikembalikan membutuhkan memori (dalam RAM atau dalam HDD).
sumber
Jika Anda mengembalikan referensi array atau string, maka dunia luar dapat memodifikasi konten dalam objek itu, dan karenanya menjadikannya sebagai objek yang dapat diubah (dapat dimodifikasi).
sumber
Berarti abadi tidak bisa diubah, dan bisa berubah berarti Anda bisa berubah.
Objek berbeda dari primitif di Jawa. Primitif dibangun dalam tipe (boolean, int, dll) dan objek (kelas) adalah tipe yang dibuat pengguna.
Primitif dan objek dapat berubah atau tidak berubah ketika didefinisikan sebagai variabel anggota dalam implementasi kelas.
Banyak orang berpikir primitif dan variabel objek memiliki pengubah akhir di depan mereka tidak berubah, namun, ini tidak sepenuhnya benar. Jadi akhir hampir tidak berarti tidak berubah untuk variabel. Lihat contoh di sini
http://www.siteconsortium.com/h/D0000F.php .
sumber
Immutable object
- adalah keadaan objek yang tidak dapat diubah setelah pembuatan. Objek tidak berubah ketika semua bidangnya tidak berubahAman utas
Keuntungan utama dari objek yang tidak dapat diubah adalah bahwa benda itu alami untuk lingkungan bersamaan. Masalah terbesar dalam konkurensi adalah
shared resource
yang dapat diubah setiap utas. Tetapi jika suatu objek tidak berubah itu adalahread-only
operasi yang aman thread. Setiap modifikasi dari objek abadi asli mengembalikan salinanefek samping gratis
Sebagai pengembang, Anda sepenuhnya yakin bahwa keadaan objek yang tidak dapat diubah tidak dapat diubah dari tempat mana pun (sengaja atau tidak)
kompilasi optimasi
Meningkatkan kinerja
Kerugian:
Menyalin objek adalah operasi yang lebih berat daripada mengubah objek yang bisa berubah, itulah sebabnya ia memiliki beberapa jejak kinerja
Untuk membuat
immutable
objek, Anda harus menggunakan:Tingkat bahasa Setiap bahasa berisi alat untuk membantu Anda. Misalnya Java memiliki
final
danprimitives
, Swift memilikilet
danstruct
[Tentang] . Bahasa mendefinisikan jenis variabel. Misalnya Java memilikiprimitive
danreference
mengetik, Swift memilikivalue
danreference
mengetik [Tentang] . Untuk objek yang tidak dapat diubah, lebih nyaman adalahprimitives
danvalue
ketik yang membuat salinan secara default. Adapunreference
jenis itu lebih sulit (karena Anda dapat mengubah keadaan objek dari itu) tetapi mungkin. Misalnya Anda dapat menggunakanclone
pola di tingkat pengembangTingkat pengembang. Sebagai pengembang, Anda tidak harus menyediakan antarmuka untuk perubahan status
sumber