Jika benda tidak bergerak baik, mengapa orang terus membuat benda bisa berubah? [Tutup]

250

Jika objek abadiut bagus, sederhana dan menawarkan manfaat dalam pemrograman bersamaan, mengapa programmer terus membuat objek yang bisa berubah²?

Saya memiliki empat tahun pengalaman dalam pemrograman Java dan seperti yang saya lihat, hal pertama yang dilakukan orang setelah membuat kelas adalah menghasilkan getter dan setter dalam IDE (sehingga membuatnya bisa berubah). Apakah ada kesadaran yang kurang atau bisakah kita menggunakan benda-benda yang bisa berubah dalam kebanyakan skenario?


¹ objek Berubah adalah obyek yang negara tidak dapat diubah setelah dibuat.
² Objek yang dapat berubah-ubah adalah objek yang dapat dimodifikasi setelah dibuat.

ahsteele
sumber
42
Saya berpikir bahwa, terlepas dari alasan yang sah (seperti yang disebutkan oleh Péter di bawah), "pengembang malas" adalah alasan yang lebih umum daripada pengembang "bodoh". Dan sebelum "pengembang bodoh" juga ada "pengembang tidak informasi"
Joachim Sauer
201
Untuk setiap programmer / blogger evangelis ada 1000 pembaca blog yang giat yang segera menemukan kembali diri mereka sendiri dan mengadopsi teknik terbaru. Untuk setiap dari mereka ada 10.000 programmer di luar sana dengan hidung mereka ke batu gerinda mendapatkan pekerjaan sehari-hari dan mendapatkan produk dari pintu. Orang-orang itu menggunakan teknik yang sudah teruji dan terpercaya yang telah bekerja untuk mereka selama bertahun-tahun. Mereka menunggu sampai teknik baru diadopsi secara luas dan menunjukkan manfaat yang sebenarnya sebelum mengambilnya. Jangan menyebut mereka bodoh, dan mereka malas, panggil mereka "sibuk".
Binary Worrier
22
@BinaryWorrier: objek yang tidak dapat diubah bukanlah "hal baru". Mereka mungkin tidak banyak digunakan untuk objek domain, tetapi Java dan C # memilikinya sejak awal. Juga: "malas" tidak selalu merupakan kata yang buruk, beberapa jenis "malas" adalah keuntungan mutlak bagi pengembang.
Joachim Sauer
11
@ Joachim: Saya pikir itu cukup jelas bahwa "malas" digunakan dalam pengertian yang merendahkan di atas :) Juga, Objek yang Tidak Berubah (seperti Lambda Calculus, dan OOP kembali pada hari itu - ya saya setua itu) tidak perlu menjadi baru tiba-tiba menjadi cita rasa bulan ini . Saya tidak berargumen bahwa mereka adalah hal yang buruk (mereka tidak), atau bahwa mereka tidak memiliki tempat mereka (mereka jelas melakukannya), tenang saja pada orang-orang karena mereka belum pernah mendengar Good Word terbaru dan telah dikonversi dengan sungguh-sungguh sendiri (tidak menyalahkan Anda untuk komentar "malas", saya tahu Anda mencoba untuk meringankannya).
Binary Worrier
116
-1, objek abadi tidak 'baik'. Lebih atau kurang tepat untuk situasi tertentu. Siapa pun yang memberi tahu Anda satu teknik atau lainnya secara objektif 'baik' atau 'buruk' di atas yang lain untuk semua situasi adalah menjual Anda agama.
GrandmasterB

Jawaban:

326

Kedua objek yang dapat berubah dan tidak berubah memiliki kegunaan, pro dan kontra sendiri.

Benda-benda yang tidak dapat berubah memang membuat hidup lebih sederhana dalam banyak kasus. Mereka terutama berlaku untuk tipe nilai, di mana objek tidak memiliki identitas sehingga mereka dapat dengan mudah diganti. Dan mereka dapat membuat pemrograman bersamaan lebih aman dan bersih (kebanyakan bug konkurensi yang paling sulit ditemukan pada akhirnya disebabkan oleh keadaan yang bisa berubah yang dibagikan di antara utas). Namun, untuk objek besar dan / atau kompleks, membuat salinan objek baru untuk setiap perubahan tunggal bisa sangat mahal dan / atau membosankan. Dan untuk objek dengan identitas berbeda, mengubah objek yang sudah ada jauh lebih sederhana dan intuitif daripada membuat salinannya yang baru dan dimodifikasi.

Pikirkan tentang karakter permainan. Dalam gim, kecepatan adalah prioritas utama, jadi mewakili karakter gim Anda dengan objek yang bisa berubah kemungkinan besar akan membuat gim Anda berjalan secara signifikan lebih cepat daripada implementasi alternatif tempat salinan baru gim karakter dihasilkan untuk setiap perubahan kecil.

Selain itu, persepsi kita tentang dunia nyata tak terhindarkan didasarkan pada objek yang bisa berubah. Ketika Anda mengisi mobil Anda dengan bahan bakar di pompa bensin, Anda menganggapnya sebagai objek yang sama selama ini (yaitu identitasnya dipertahankan ketika kondisinya berubah) - tidak seolah-olah mobil tua dengan tangki kosong diganti dengan yang baru berturut-turut contoh mobil memiliki tangki mereka secara bertahap semakin penuh. Jadi, setiap kali kita memodelkan beberapa domain dunia nyata dalam suatu program, biasanya lebih mudah dan lebih mudah untuk menerapkan model domain menggunakan objek yang bisa berubah untuk mewakili entitas dunia nyata.

Terlepas dari semua alasan yang sah ini, sayangnya, penyebab paling mungkin mengapa orang terus membuat objek yang bisa berubah adalah kelembaman pikiran, alias penolakan untuk berubah. Perhatikan bahwa sebagian besar pengembang saat ini telah dilatih sebelum kekekalan (dan paradigma yang mengandung, pemrograman fungsional) menjadi "trendi" dalam lingkup pengaruhnya, dan tidak memperbarui pengetahuan mereka tentang alat dan metode baru dalam perdagangan kami - pada kenyataannya, banyak dari kita manusia yang secara positif menentang ide dan proses baru. "Saya telah pemrograman seperti ini selama nn tahun dan saya tidak peduli tentang mode bodoh terbaru!"

Péter Török
sumber
27
Tepat sekali. Khususnya dalam pemrograman GUI, objek yang bisa berubah sangat berguna.
Florian Salihovic
23
Ini bukan hanya perlawanan, saya yakin banyak devs akan senang untuk mencoba yang terbaru dan terhebat, tetapi seberapa sering proyek-proyek baru berputar di lingkungan rata-rata dev di mana mereka dapat menerapkan praktik-praktik baru ini? Tidak pernah ada yang bisa atau akan menulis proyek hobi hanya untuk mencoba keadaan abadi.
Steven Evers
29
Dua peringatan kecil untuk ini: (1) Ambil karakter permainan yang bergerak. The Pointkelas di NET misalnya adalah kekal tetapi menciptakan poin baru sebagai hasil dari perubahan mudah dan dengan demikian terjangkau. Animasi karakter yang tidak dapat diubah dapat dibuat sangat murah dengan memisahkan “bagian yang bergerak” (tapi ya, beberapa aspek kemudian dapat berubah). (2) "objek besar dan / atau kompleks" dapat berubah dengan sangat baik. String sering kali berukuran besar, dan biasanya mendapat manfaat dari kekekalan. Saya pernah menulis ulang kelas grafik yang kompleks agar tidak berubah, membuat kode lebih sederhana dan lebih efisien. Dalam kasus seperti itu, memiliki pembangun yang bisa berubah adalah kuncinya.
Konrad Rudolph
4
@KonradRudolph, poin bagus, terima kasih. Saya tidak bermaksud mengesampingkan penggunaan imutabilitas pada objek yang kompleks, tetapi menerapkan kelas seperti itu dengan benar dan efisien jauh dari tugas yang sepele, dan upaya ekstra yang diperlukan mungkin tidak selalu dapat dibenarkan.
Péter Török
6
Anda membuat poin bagus tentang status vs identitas. Inilah sebabnya mengapa Rich Hickey (penulis Clojure) memisahkan keduanya di Clojure. Orang bisa berargumen, bahwa mobil yang Anda miliki dengan 1/2 tangki bensin tidak sama dengan mobil yang memiliki 1/4 tangki bensin. Mereka memiliki identitas yang sama, tetapi mereka tidak sama, setiap "tanda" pada waktu realitas kita menciptakan tiruan dari setiap objek di dunia kita, otak kita kemudian hanya menyatukannya dengan identitas yang sama. Clojure memiliki referensi, atom, agen, dll. Untuk mewakili waktu. Dan peta, vektor, dan daftar untuk waktu aktual.
Timothy Baldridge
130

Saya pikir Anda semua melewatkan jawaban yang paling jelas. Sebagian besar pengembang membuat objek yang dapat diubah karena mutabilitas adalah default dalam bahasa imperatif. Sebagian besar dari kita memiliki hal-hal yang lebih baik untuk dilakukan dengan waktu kita daripada terus-menerus mengubah kode dari default - lebih benar atau tidak. Dan ketidakberdayaan bukanlah obat mujarab seperti pendekatan lainnya. Itu membuat beberapa hal lebih mudah tetapi membuat orang lain jauh lebih sulit karena beberapa jawaban telah ditunjukkan.

Onorio Catenacci
sumber
9
Dalam sebagian besar IDE modern yang saya tahu, dibutuhkan upaya yang hampir sama untuk menghasilkan hanya getter, seperti untuk menghasilkan getter dan setter. Meskipun benar bahwa menambahkan final, constdll memerlukan sedikit usaha ekstra ... kecuali jika Anda membuat template kode :-)
Péter Török
10
@ PéterTörök ini bukan hanya upaya tambahan - ini adalah fakta bahwa sesama pembuat kode Anda ingin menggantung Anda dalam patung karena mereka menemukan gaya pengkodean Anda sangat asing dengan pengalaman mereka. Itu juga mencegah hal semacam ini.
Onorio Catenacci
5
Itu mungkin diatasi dengan lebih banyak komunikasi dan pendidikan, misalnya disarankan untuk berbicara atau memberikan presentasi kepada rekan satu tim tentang gaya pengkodean baru sebelum benar-benar memasukkannya ke dalam basis kode yang ada. Memang benar bahwa proyek yang baik memiliki gaya pengkodean umum yang bukan hanya campuran dari gaya pengkodean yang disukai oleh setiap anggota proyek (dulu dan sekarang). Jadi, memperkenalkan atau mengubah idiom pengkodean harus menjadi keputusan tim.
Péter Török
3
@ PéterTörök Dalam bahasa imperatif objek yang bisa berubah adalah perilaku default. Jika Anda hanya menginginkan objek yang tidak dapat diubah, yang terbaik adalah beralih ke bahasa fungsional.
emory
3
@ PéterTörök Agak naif untuk berasumsi bahwa menggabungkan immutability ke dalam sebuah program tidak lebih dari menjatuhkan semua setter. Anda masih memerlukan cara agar keadaan program berubah secara bertahap, dan untuk ini Anda membutuhkan pembangun, atau ketidakmampuan es loli, atau proksi yang bisa berubah, yang semuanya membutuhkan sekitar 1 miliar kali upaya menjatuhkan para pemukim.
Asad Saeeduddin
49
  1. Ada tempat untuk berubah-ubah. Prinsip-prinsip desain yang didorong oleh domain memberikan pemahaman yang kuat tentang apa yang harus bisa berubah dan apa yang harus tidak berubah. Jika Anda memikirkannya, Anda akan menyadari bahwa tidak praktis untuk membayangkan suatu sistem di mana setiap perubahan keadaan menjadi suatu objek memerlukan penghancuran dan komposisi ulang dari itu, dan untuk setiap objek yang mereferensikannya. Dengan sistem yang kompleks, hal ini dapat dengan mudah menyapu dan membangun kembali seluruh objek grafik sistem

  2. Sebagian besar pengembang tidak membuat apa pun di mana persyaratan kinerja cukup signifikan sehingga mereka perlu fokus pada konkurensi (atau banyak masalah lain yang secara universal dianggap sebagai praktik yang baik oleh informasi).

  3. Ada beberapa hal yang tidak bisa Anda lakukan dengan objek abadi, seperti memiliki hubungan dua arah. Setelah Anda menetapkan nilai asosiasi pada satu objek, itu akan berubah identitas. Jadi, Anda menetapkan nilai baru pada objek lain dan juga berubah. Masalahnya adalah referensi objek pertama tidak lagi valid, karena instance baru telah dibuat untuk mewakili objek dengan referensi. Melanjutkan ini hanya akan menghasilkan regresi tanpa batas. Saya melakukan sedikit studi kasus setelah membaca pertanyaan Anda, begini tampilannya. Apakah Anda memiliki pendekatan alternatif yang memungkinkan fungsi seperti itu sambil mempertahankan keabadian?

        public class ImmutablePerson { 
    
         public ImmutablePerson(string name, ImmutableEventList eventsToAttend)
         {
              this.name = name;
              this.eventsToAttend = eventsToAttend;
         }
         private string name;
         private ImmutableEventList eventsToAttend;
    
         public string Name { get { return this.name; } }
    
         public ImmutablePerson RSVP(ImmutableEvent immutableEvent){
             // the person is RSVPing an event, thus mutating the state 
             // of the eventsToAttend.  so we need a new person with a reference
             // to the new Event
             ImmutableEvent newEvent = immutableEvent.OnRSVPReceived(this);
             ImmutableEventList newEvents = this.eventsToAttend.Add(newEvent));
             var newSelf = new ImmutablePerson(name, newEvents);
             return newSelf;
         }
        }
    
        public class ImmutableEvent { 
         public ImmutableEvent(DateTime when, ImmutablePersonList peopleAttending, ImmutablePersonList peopleNotAttending){
             this.when = when;     
             this.peopleAttending = peopleAttending;
             this.peopleNotAttending = peopleNotAttending;
         }
         private DateTime when; 
         private ImmutablePersonList peopleAttending;
         private ImmutablePersonList peopleNotAttending;
         public ImmutableEvent OnReschedule(DateTime when){
               return new ImmutableEvent(when,peopleAttending,peopleNotAttending);
         }
         //  notice that this will be an infinite loop, because everytime one counterpart
         //  of the bidirectional relationship is added, its containing object changes
         //  meaning it must re construct a different version of itself to 
         //  represent the mutated state, the other one must update its
         //  reference thereby obsoleting the reference of the first object to it, and 
         //  necessitating recursion
         public ImmutableEvent OnRSVPReceived(ImmutablePerson immutablePerson){
               if(this.peopleAttending.Contains(immutablePerson)) return this;
               ImmutablePersonList attending = this.peopleAttending.Add(immutablePerson);
               ImmutablePersonList notAttending = this.peopleNotAttending.Contains( immutablePerson ) 
                                    ? peopleNotAttending.Remove(immutablePerson)
                                    : peopleNotAttending;
               return new ImmutableEvent(when, attending, notAttending);
         }
        }
        public class ImmutablePersonList
        {
          private ImmutablePerson[] immutablePeople;
          public ImmutablePersonList(ImmutablePerson[] immutablePeople){
              this.immutablePeople = immutablePeople;
          }
          public ImmutablePersonList Add(ImmutablePerson newPerson){
              if(this.Contains(newPerson)) return this;
              ImmutablePerson[] newPeople = new ImmutablePerson[immutablePeople.Length];
              for(var i=0;i<immutablePeople.Length;i++)
                  newPeople[i] = this.immutablePeople[i];
              newPeople[immutablePeople.Length] = newPerson;
          }
          public ImmutablePersonList Remove(ImmutablePerson newPerson){
              if(immutablePeople.IndexOf(newPerson) != -1)
              ImmutablePerson[] newPeople = new ImmutablePerson[immutablePeople.Length-2];
              bool hasPassedRemoval = false;
              for(var i=0;i<immutablePeople.Length;i++)
              {
                 hasPassedRemoval = hasPassedRemoval || immutablePeople[i] == newPerson;
                 newPeople[i] = this.immutablePeople[hasPassedRemoval ? i + 1 : i];
              }
              return new ImmutablePersonList(newPeople);
          }
          public bool Contains(ImmutablePerson immutablePerson){ 
             return this.immutablePeople.IndexOf(immutablePerson) != -1;
          } 
        }
        public class ImmutableEventList
        {
          private ImmutableEvent[] immutableEvents;
          public ImmutableEventList(ImmutableEvent[] immutableEvents){
              this.immutableEvents = immutableEvents;
          }
          public ImmutableEventList Add(ImmutableEvent newEvent){
              if(this.Contains(newEvent)) return this;
              ImmutableEvent[] newEvents= new ImmutableEvent[immutableEvents.Length];
              for(var i=0;i<immutableEvents.Length;i++)
                  newEvents[i] = this.immutableEvents[i];
              newEvents[immutableEvents.Length] = newEvent;
          }
          public ImmutableEventList Remove(ImmutableEvent newEvent){
              if(immutableEvents.IndexOf(newEvent) != -1)
              ImmutableEvent[] newEvents = new ImmutableEvent[immutableEvents.Length-2];
              bool hasPassedRemoval = false;
              for(var i=0;i<immutablePeople.Length;i++)
              {
                 hasPassedRemoval = hasPassedRemoval || immutableEvents[i] == newEvent;
                 newEvents[i] = this.immutableEvents[hasPassedRemoval ? i + 1 : i];
              }
              return new ImmutableEventList(newPeople);
          }
          public bool Contains(ImmutableEvent immutableEvent){ 
             return this.immutableEvent.IndexOf(immutableEvent) != -1;
          } 
        }
    
smartcaveman
sumber
2
@AndresF., Jika Anda memiliki pandangan berbeda tentang cara mempertahankan grafik kompleks dengan hubungan dua arah menggunakan hanya objek yang tidak dapat diubah, maka saya akan senang mendengarnya. (Saya berasumsi kita bisa sepakat bahwa koleksi / array adalah objek)
smartcaveman
2
@AndresF., (1) Pernyataan pertama saya tidak universal sehingga tidak salah. Saya benar-benar memberikan contoh kode untuk menjelaskan bagaimana hal itu benar dalam kasus-kasus tertentu yang sangat umum dalam pengembangan aplikasi. (2) Hubungan dua arah membutuhkan mutabilitas, secara umum. Saya tidak percaya bahwa Jawa adalah biang keladinya. Seperti yang saya katakan, saya akan dengan senang hati mengevaluasi setiap alternatif konstruktif yang Anda sarankan, tetapi pada titik ini komentar Anda terdengar seperti "Anda salah karena saya bilang begitu".
smartcaveman
14
@smartcaveman Tentang (2), saya juga tidak setuju: secara umum, "hubungan dua arah" adalah konsep matematika ortogonal untuk mutabilitas. Seperti biasanya diimplementasikan di Jawa itu memang membutuhkan mutabilitas (saya setuju dengan Anda tentang hal itu). Namun, saya bisa memikirkan implementasi alternatif: kelas hubungan antara dua objek, dengan konstruktor Relationship(a, b); pada titik menciptakan hubungan, kedua entitas a& bsudah ada, dan hubungan itu sendiri juga tidak berubah. Saya tidak mengatakan pendekatan ini praktis di Jawa; Hanya saja itu mungkin.
Andres F.
4
@AndresF., Jadi, berdasarkan apa yang Anda katakan, Jika Radalah Relationship(a,b)dan keduanya adan btidak berubah, tidak ajuga tidak bakan memiliki referensi R. Agar ini berfungsi, referensi harus disimpan di tempat lain (seperti kelas statis). Apakah saya memahami maksud Anda dengan benar?
smartcaveman
9
Dimungkinkan untuk menyimpan hubungan dua arah untuk data yang tidak dapat diubah sebagaimana ditunjukkan oleh Chthulhu melalui kemalasan. Berikut adalah salah satu cara untuk melakukan ini: haskell.org/haskellwiki/Tying_the_Knot
Thomas Eding
36

Saya telah membaca "struktur data murni fungsional", dan itu membuat saya menyadari bahwa ada beberapa struktur data yang jauh lebih mudah diimplementasikan dengan menggunakan objek yang bisa berubah.

Untuk menerapkan pohon pencarian biner, Anda harus mengembalikan pohon baru setiap kali: Pohon baru Anda harus membuat salinan dari setiap node yang telah dimodifikasi (cabang-cabang yang tidak dimodifikasi dibagikan). Untuk fungsi insert Anda ini tidak terlalu buruk, tetapi bagi saya, banyak hal menjadi tidak efisien dengan cepat ketika saya mulai bekerja pada delete dan re-balance.

Hal lain yang perlu disadari adalah bahwa Anda dapat bertahun-tahun menulis kode berorientasi objek, dan tidak pernah benar-benar menyadari betapa buruknya keadaan yang dapat ditukar yang dibagikan, jika kode Anda tidak dijalankan dengan cara yang akan memunculkan masalah konkurensi.

Paul Sanwald
sumber
3
Apakah ini buku Okasaki?
Siput mekanik
ya. agak kering tapi satu ton info bagus ...
Paul Sanwald
4
Lucu, saya selalu berpikir pohon merah / hitam Okasakis jauh lebih sederhana. 10 baris atau lebih. Saya kira keuntungan terbesar adalah ketika Anda benar-benar ingin mempertahankan versi lama juga.
Thomas Ahle
Meskipun kalimat terakhir mungkin benar di masa lalu, tidak jelas apakah akan tetap benar di masa depan, mengingat tren perangkat keras saat ini, dll.
jk.
29

Dari sudut pandang saya, itu adalah kurangnya kesadaran. Jika Anda melihat bahasa JVM lain yang dikenal (Scala, Clojure), objek yang bisa berubah jarang terlihat dalam kode dan itulah sebabnya orang mulai menggunakannya dalam skenario di mana threading tunggal tidak cukup.

Saat ini saya sedang belajar Clojure dan memiliki sedikit pengalaman dalam Scala (4 tahun + di Jawa juga) dan gaya pengkodean Anda berubah karena kesadaran negara.

Florian Salihovic
sumber
Mungkin "dikenal" daripada "populer" akan menjadi pilihan kata yang lebih baik.
Den
Ya itu betul.
Florian Salihovic
5
+1: Saya setuju: setelah mempelajari beberapa Scala dan Haskell, saya cenderung menggunakan final di Java dan const di C ++ di mana-mana. Saya juga menggunakan benda-benda yang tidak dapat berubah jika memungkinkan dan, sementara benda-benda yang bisa berubah masih sangat sering dibutuhkan, sungguh menakjubkan betapa seringnya Anda dapat menggunakan benda-benda yang tidak berubah.
Giorgio
5
Saya membaca komentar saya ini setelah dua setengah tahun, dan pendapat saya telah berubah mendukung ketidakberubahan. Dalam proyek saya saat ini (yang dalam Python) kami menggunakan objek yang bisa berubah sangat sangat jarang. Bahkan data tetap kami tidak dapat diubah: kami membuat catatan baru sebagai hasil dari beberapa operasi dan menghapus catatan lama ketika tidak diperlukan lagi, tetapi kami tidak pernah memperbarui catatan apa pun pada disk. Tidak perlu dikatakan, ini telah membuat aplikasi multiuser kami secara bersamaan lebih mudah untuk diimplementasikan dan dikelola hingga sekarang.
Giorgio
13

Saya pikir salah satu faktor utama telah diabaikan: Java Beans sangat bergantung pada gaya tertentu dari bermutasi objek, dan (terutama mengingat sumber) beberapa orang tampaknya menganggap itu sebagai (atau bahkan yang ) contoh kanonik bagaimana semua Java harus ditulis.

Jerry Coffin
sumber
4
+1, pola pengambil / penyetel digunakan terlalu sering karena beberapa jenis implementasi standar setelah analisis data pertama.
Jaap
Ini mungkin poin besar ... "karena itulah yang dilakukan orang lain" ... jadi pasti benar. Untuk program "Hello World" itu mungkin paling mudah. Mengelola perubahan-objek objek melalui banyak properti yang dapat berubah ... yang sedikit lebih rumit daripada kedalaman pemahaman "Hello World". 20 tahun kemudian saya sangat terkejut bahwa puncak pemrograman abad ke-20 adalah menulis metode getX dan setX (betapa membosankan) atas setiap atribut objek tanpa struktur sama sekali. Hanya satu langkah lagi dari akses langsung ke properti publik dengan 100% kemampuan berubah-ubah.
Darrell Teague
12

Setiap sistem Java perusahaan yang saya kerjakan dalam karier saya menggunakan Hibernate atau Java Persistence API (JPA). Hibernate dan JPA pada dasarnya menentukan bahwa sistem Anda menggunakan objek yang bisa berubah, karena seluruh premisnya adalah mereka mendeteksi dan menyimpan perubahan pada objek data Anda. Bagi banyak proyek, kemudahan pengembangan yang dibawa Hibernate lebih menarik daripada manfaat objek yang tidak dapat diubah.

Jelas objek yang bisa berubah telah ada jauh lebih lama dari Hibernate, jadi Hibernate mungkin bukan 'penyebab' asli dari popularitas objek yang bisa berubah. Mungkin popularitas objek yang dapat diubah memungkinkan Hibernate berkembang.

Tapi hari ini jika banyak programmer junior memotong gigi mereka pada sistem perusahaan menggunakan Hibernate atau ORM lain maka mungkin mereka akan mengambil kebiasaan menggunakan objek yang bisa berubah. Kerangka kerja seperti Hibernate mungkin membudaya popularitas objek yang bisa berubah.

belanda
sumber
Poin luar biasa. Untuk menjadi segalanya bagi semua orang, kerangka kerja seperti itu tidak punya banyak pilihan selain turun ke penyebut umum terendah, menggunakan proxy berbasis refleksi dan mendapatkan / mengatur jalan mereka ke fleksibilitas. Tentu saja ini menciptakan sistem dengan sedikit atau tanpa aturan transisi negara atau cara umum yang digunakan untuk mengimplementasikannya sayangnya. Saya tidak sepenuhnya yakin setelah banyak proyek sekarang apa yang lebih baik untuk kelayakan, ekstensibilitas dan kebenaran. Saya cenderung berpikir hibrida. Kebaikan ORM dinamis tetapi dengan beberapa definisi bidang mana yang diperlukan dan perubahan status apa yang mungkin dilakukan.
Darrell Teague
9

Poin utama yang belum disebutkan adalah bahwa keadaan suatu objek bisa berubah memungkinkan untuk memiliki identitas objek yang merangkum keadaan itu menjadi abadi.

Banyak program dirancang untuk memodelkan hal-hal dunia nyata yang secara inheren bisa berubah. Misalkan pada pukul 12.51 pagi, beberapa variabel AllTrucksmemegang referensi ke objek # 451, yang merupakan akar dari struktur data yang menunjukkan kargo apa yang terkandung dalam semua truk armada pada saat itu (12:51 pagi), dan beberapa variabel BobsTruckdapat digunakan untuk mendapatkan referensi ke objek # 24601 menunjuk ke objek yang menunjukkan kargo apa yang terkandung dalam truk Bob pada saat itu (12:51 pagi). Pada pukul 12:52 pagi, beberapa truk (termasuk Bob) dimuat dan dibongkar, dan struktur data diperbarui sehingga AllTruckssekarang akan memiliki referensi ke struktur data yang menunjukkan muatan berada di semua truk pada pukul 12:52.

Apa yang harus terjadi BobsTruck?

Jika properti 'kargo' dari setiap objek truk tidak dapat diubah, maka objek # 24601 akan selamanya mewakili keadaan yang dimiliki truk Bob pada pukul 12:51 pagi. Jika BobsTruckmemegang referensi langsung ke objek # 24601, maka kecuali kode pembaruan yang AllTrucksjuga terjadi untuk memperbarui BobsTruck, itu akan berhenti mewakili keadaan truk Bob saat ini. Perhatikan lebih lanjut bahwa kecuali BobsTruckdisimpan dalam suatu bentuk objek yang dapat diubah, satu-satunya cara agar pembaruan yang diperbarui AllTrucksadalah jika kode diprogram secara eksplisit untuk melakukannya.

Jika seseorang ingin dapat menggunakan BobsTruckuntuk mengamati truk Bob negara sambil tetap menjaga semua benda tetap, seseorang bisa BobsTruckmenjadi fungsi abadi yang, mengingat nilai yang AllTruckstelah atau miliki pada waktu tertentu, akan menghasilkan keadaan truk Bob di waktu itu. Satu bahkan dapat memilikinya memegang sepasang fungsi abadi - salah satunya akan seperti di atas, dan yang lainnya akan menerima referensi ke keadaan armada dan keadaan truk baru, dan mengembalikan referensi ke keadaan armada baru yang cocok dengan yang lama, kecuali bahwa truk Bob akan memiliki negara baru.

Sayangnya, harus menggunakan fungsi seperti itu setiap kali seseorang ingin mengakses keadaan truk Bob bisa menjadi agak menjengkelkan dan rumit. Pendekatan alternatif akan mengatakan bahwa objek # 24601 akan selalu dan selamanya (selama orang memegang referensi untuk itu) mewakili saat keadaan truk Bob. Kode yang ingin berulang kali mengakses kondisi truk Bob saat ini tidak harus menjalankan fungsi yang menghabiskan waktu setiap kali - itu bisa dengan mudah melakukan fungsi pencarian sekali untuk mengetahui bahwa objek # 24601 adalah truk Bob, dan kemudian cukup akses objek itu kapan saja ia ingin melihat kondisi truk Bob saat ini.

Perhatikan bahwa pendekatan fungsional bukan tanpa keuntungan dalam lingkungan single-threaded, atau dalam lingkungan multi-threaded di mana thread sebagian besar hanya akan mengamati data daripada mengubahnya. Setiap pengamat utas yang menyalin referensi objek yang terkandungAllTrucksdan kemudian memeriksa keadaan truk yang diwakili dengan demikian akan melihat keadaan semua truk pada saat itu meraih referensi. Setiap kali utas pengamat ingin melihat data yang lebih baru, itu hanya dapat mengambil kembali referensi. Di sisi lain, memiliki seluruh keadaan armada yang diwakili oleh satu objek abadi akan menghalangi kemungkinan dua utas memperbarui truk yang berbeda secara bersamaan, karena setiap utas jika dibiarkan pada perangkatnya sendiri akan menghasilkan objek "armada negara" baru yang termasuk kondisi baru truknya dan kondisi lama lainnya. Kebenaran dapat dipastikan jika masing-masing utas menggunakan CompareExchangeuntuk memperbarui AllTruckshanya jika itu tidak berubah, dan menanggapi gagalCompareExchangedengan meregenerasi objek keadaannya dan mencoba kembali operasi, tetapi jika lebih dari satu utas mencoba operasi penulisan simultan, kinerja umumnya akan lebih buruk daripada jika semua penulisan dilakukan pada satu utas; semakin banyak utas mencoba operasi simultan tersebut, semakin buruk kinerjanya.

Jika objek truk individu dapat berubah tetapi memiliki identitas yang tidak dapat diubah , skenario multi-ulir menjadi lebih bersih. Hanya satu utas yang diizinkan beroperasi pada suatu waktu pada truk tertentu, tetapi utas yang beroperasi pada truk yang berbeda dapat melakukannya tanpa gangguan. Meskipun ada beberapa cara seseorang dapat meniru perilaku seperti itu bahkan ketika menggunakan objek yang tidak dapat diubah (misalnya seseorang dapat mendefinisikan objek "AllTrucks" sehingga pengaturan keadaan truk milik XXX ke SSS hanya akan memerlukan menghasilkan objek yang mengatakan "Pada [Waktu], keadaan truk milik [XXX] sekarang [SSS]; keadaan segalanya adalah [Nilai lama AllTrucks] ". Menghasilkan objek seperti itu akan cukup cepat sehingga bahkan di hadapan pertengkaran, sebuahCompareExchangeloop tidak akan lama. Di sisi lain, menggunakan struktur data seperti itu akan secara substansial meningkatkan jumlah waktu yang dibutuhkan untuk menemukan truk orang tertentu. Menggunakan objek yang bisa berubah dengan identitas yang tidak berubah menghindari masalah itu.

supercat
sumber
8

Tidak ada benar atau salah, itu tergantung apa yang Anda inginkan. Ada alasan mengapa beberapa orang lebih suka bahasa yang mendukung satu paradigma di atas yang lain, dan satu model data lebih dari yang lain. Itu hanya tergantung pada preferensi Anda, dan pada apa yang ingin Anda capai (dan dapat dengan mudah menggunakan kedua pendekatan tanpa mengasingkan penggemar berat dari satu sisi atau sisi lain adalah grail suci yang dicari beberapa bahasa).

Saya pikir cara terbaik dan tercepat untuk menjawab pertanyaan Anda adalah bagi Anda untuk memimpin Pro dan Kontra Kekekalan vs Mutabilitas .

haylem
sumber
7

Periksa posting blog ini: http://www.yegor256.com/2014/06/09/objects-should-be-immutable.html . Ini meringkas mengapa benda tidak berubah lebih baik daripada bisa berubah. Berikut daftar argumen singkat:

  • objek abadi lebih mudah untuk dibangun, diuji, dan digunakan
  • objek yang benar-benar abadi selalu aman dari benang
  • mereka membantu untuk menghindari penggabungan waktu
  • penggunaannya bebas efek samping (tidak ada salinan defensif)
  • masalah mutabilitas identitas dihindari
  • mereka selalu memiliki atomisitas kegagalan
  • mereka lebih mudah untuk di-cache

Orang-orang menggunakan objek yang bisa berubah, sejauh yang saya mengerti, karena mereka masih mencampurkan OOP dengan pemrograman prosedural imperatif.

yegor256
sumber
5

Dalam Java objek yang tidak dapat diubah memerlukan konstruktor yang akan mengambil semua properti objek (atau konstruktor membuatnya dari argumen atau default lain). Properti itu harus ditandai sebagai final .

Ada empat masalah dengan ini sebagian besar berkaitan dengan pengikatan data :

  1. Meta-data refleksi Konstruktor Java tidak mempertahankan nama argumen.
  2. Konstruktor Java (dan metode) tidak memiliki parameter bernama (juga disebut label) sehingga membingungkan dengan banyak parameter.
  3. Ketika mewarisi objek abadi yang lain, urutan konstruktor yang tepat harus dipanggil. Ini bisa agak sulit sampai menyerah dan meninggalkan salah satu bidang non-final.
  4. Sebagian besar teknologi yang mengikat (seperti Spring MVC Binding Data, Hibernate, dll ...) hanya akan bekerja dengan konstruktor default no-arg (ini karena anotasi tidak selalu ada).

Anda dapat mengurangi # 1 dan # 2 menggunakan anotasi seperti @ConstructorPropertiesdan membuat objek pembangun yang dapat berubah (biasanya fasih) untuk membuat objek yang tidak dapat diubah.

Adam Gent
sumber
5

Saya terkejut bahwa tidak ada yang menyebutkan manfaat optimalisasi kinerja. Bergantung pada bahasa, kompiler dapat membuat banyak optimisasi ketika berhadapan dengan data yang tidak dapat diubah karena tahu data tidak akan pernah berubah. Segala macam hal terlewati, yang memberi Anda manfaat kinerja yang luar biasa.

Juga objek yang tidak berubah hampir menghilangkan seluruh kelas bug negara.

Ini tidak sebesar yang seharusnya karena itu lebih sulit, tidak berlaku untuk setiap bahasa dan kebanyakan orang diajarkan kode imperatif.

Saya juga telah menemukan bahwa sebagian besar programmer senang dalam kotak mereka dan sering menolak ide-ide baru yang mereka tidak sepenuhnya mengerti. Secara umum orang tidak suka perubahan.

Juga ingat, bahwa keadaan kebanyakan programmer buruk. Kebanyakan pemrograman yang dilakukan di alam liar mengerikan dan disebabkan oleh kurangnya pemahaman dan politik.

Jay
sumber
4

Mengapa orang menggunakan fitur yang kuat? Mengapa orang menggunakan pemrograman meta, kemalasan, atau pengetikan dinamis? Jawabannya adalah kenyamanan. Keadaan yang bisa berubah sangat mudah. Sangat mudah untuk memperbarui di tempat dan ambang ukuran proyek di mana keadaan tidak berubah lebih produktif untuk bekerja dengan daripada keadaan bisa berubah cukup tinggi sehingga pilihan tidak akan menggigit Anda kembali untuk sementara waktu.

dan_waterworth
sumber
4

Bahasa pemrograman dirancang untuk dieksekusi oleh komputer. Semua blok bangunan penting komputer - CPU, RAM, cache, disk - bisa berubah. Ketika mereka tidak (BIOS), mereka benar-benar tidak dapat berubah dan Anda tidak dapat membuat objek baru juga.

Oleh karena itu, bahasa pemrograman apa pun yang dibangun di atas objek yang tidak dapat diubah akan mengalami kesenjangan representasi dalam implementasinya. Dan untuk bahasa awal seperti C, itu adalah batu sandungan besar.

MSalters
sumber
1

Tanpa objek yang bisa berubah, Anda tidak memiliki status. Harus diakui, ini adalah hal yang baik jika Anda dapat mengelolanya dan jika ada kemungkinan suatu objek dapat dirujuk dari lebih dari satu utas. Tetapi program ini akan agak membosankan. Banyak perangkat lunak, terutama server web, menghindari mengambil tanggung jawab untuk objek yang dapat berubah dengan mendorong mutabilitas pada basis data, sistem operasi, pustaka sistem, dll. Sebagai masalah praktis, ini membebaskan programmer dari masalah kemampuan berubah dan membuat web (dan lainnya) pengembangan terjangkau. Tetapi mutabilitas masih ada.

Secara umum, Anda memiliki tiga jenis kelas: kelas normal, tidak aman, yang harus dijaga dan dilindungi dengan hati-hati; kelas abadi, yang dapat digunakan secara bebas; dan kelas yang bisa diubah, thread-safe yang dapat digunakan secara bebas tetapi harus ditulis dengan sangat hati-hati. Tipe pertama adalah tipe yang merepotkan, dengan tipe terburuk adalah tipe yang dianggap tipe ketiga. Tentu saja, tipe pertama adalah yang mudah untuk ditulis.

Saya biasanya berakhir dengan banyak kelas yang normal dan bisa berubah yang harus saya perhatikan dengan sangat hati-hati. Dalam situasi multi-utas, sinkronisasi yang diperlukan memperlambat segalanya bahkan ketika saya dapat menghindari pelukan mematikan. Jadi saya biasanya membuat salinan yang tidak dapat diubah dari kelas yang bisa berubah dan memberikannya kepada siapa pun yang dapat menggunakannya. Salinan abadi yang baru diperlukan setiap kali orignal bermutasi, jadi saya membayangkan kadang-kadang saya mungkin memiliki seratus salinan asli di luar sana. Saya sangat bergantung pada Pengumpulan Sampah.

Singkatnya, objek yang tidak dapat diubah, objek yang dapat ditransmisikan baik-baik saja jika Anda tidak menggunakan banyak utas. (Tapi multithreading inflitrating di mana-mana - hati-hati!) Mereka dapat digunakan dengan aman jika Anda membatasi mereka ke variabel lokal atau menyinkronkannya dengan ketat. Jika Anda dapat menghindarinya dengan menggunakan kode orang lain yang terbukti (DB, panggilan sistem, dll.) Melakukannya. Jika Anda bisa menggunakan kelas yang tidak dapat diubah, lakukanlah. Dan saya pikir , secara umum , orang-orang entah tidak menyadari masalah multithreading atau (masuk akal) takut pada mereka dan menggunakan semua jenis trik untuk menghindari multithreading (atau lebih tepatnya, mendorong tanggung jawab untuk itu di tempat lain).

Sebagai seorang PS, saya merasakan bahwa Java getter dan setters sudah lepas kendali. Lihat ini .

RalphChapin
sumber
7
Keadaan yang tidak dapat berubah masih merupakan keadaan.
Jeremy Heiler
3
@JeremyHeiler: Benar, tapi itu keadaan sesuatu yang adalah bisa berubah. Jika tidak ada yang bermutasi, hanya ada satu keadaan, yang merupakan hal yang sama dengan tidak ada keadaan sama sekali.
RalphChapin
1

Banyak orang memiliki jawaban yang baik jadi saya ingin menunjukkan sesuatu yang Anda sentuh yang sangat jeli dan sangat benar dan belum disebutkan di tempat lain di sini.

Secara otomatis membuat setter dan getter adalah ide yang mengerikan, mengerikan, namun ini adalah cara pertama orang yang berpikir prosedural mencoba memaksa OO ke dalam pola pikir mereka. Setter dan getter, bersama dengan properti seharusnya hanya dibuat ketika Anda menemukan Anda membutuhkannya dan tidak setiap orang secara default

Bahkan walaupun Anda membutuhkan getter secara teratur, satu-satunya cara setter atau properti yang dapat ditulis harus ada dalam kode Anda adalah melalui pola pembangun di mana mereka dikunci setelah objek telah sepenuhnya instantiated.

Banyak kelas yang bisa berubah setelah penciptaan yang baik-baik saja, itu tidak seharusnya memiliki sifat-sifatnya dimanipulasi secara langsung - melainkan harus diminta untuk memanipulasi sifat-sifatnya melalui pemanggilan metode dengan logika bisnis aktual di dalamnya (Ya, seorang setter hampir sama hal yang secara langsung memanipulasi properti)

Sekarang ini tidak benar-benar berlaku untuk kode / bahasa gaya "Scripting" baik tetapi untuk kode yang Anda buat untuk orang lain dan mengharapkan orang lain untuk membaca berulang kali selama bertahun-tahun. Saya harus mulai membuat perbedaan itu belakangan ini karena saya sangat menikmati bermain-main dengan Groovy dan ada perbedaan besar dalam target.

Bill K
sumber
1

Objek yang dapat diubah digunakan ketika Anda harus menetapkan nilai kelipatan setelah instantiasi objek.

Anda seharusnya tidak memiliki konstruktor dengan, katakanlah, enam parameter. Alih-alih Anda memodifikasi objek dengan metode penyetel.

Contoh dari ini adalah objek Laporan, dengan setter untuk font, orientasi dll.

Singkatnya: bisa berubah berguna ketika Anda memiliki banyak negara untuk mengatur ke objek dan itu tidak praktis untuk memiliki tanda tangan konstruktor yang sangat panjang.

EDIT: Pola pembangun dapat digunakan membangun seluruh keadaan objek.

user61852
sumber
3
ini disajikan seolah-olah bisa berubah dan berubah setelah instantiation adalah satu-satunya cara untuk menetapkan beberapa nilai. Demi kelengkapan, perhatikan bahwa pola Builder menawarkan kemampuan yang sama atau bahkan lebih kuat dan tidak mengharuskan seseorang mengorbankan keabadian. new ReportBuilder().font("Arial").orientation("landscape").build()
nyamuk
1
"Tidak praktis untuk memiliki tanda tangan konstruktor yang sangat panjang.": Anda selalu dapat mengelompokkan parameter menjadi objek yang lebih kecil dan meneruskan objek ini sebagai parameter ke konstruktor.
Giorgio
1
@ cHao Bagaimana jika Anda ingin mengatur 10 atau 15 atribut? Juga tidak bagus bahwa metode bernama withFontmengembalikan a Report.
Tulains Córdova
1
Sejauh menetapkan 10 atau 15 atribut, kode untuk melakukannya tidak akan canggung jika (1) Java tahu bagaimana menghilangkan konstruksi semua objek perantara, dan jika (2) lagi, nama-nama distandarisasi. Itu bukan masalah dengan kekekalan; ini masalah dengan Java yang tidak tahu bagaimana melakukannya dengan baik.
cao
1
@ cHao Membuat rantai panggilan untuk 20 atribut buruk. Kode jelek cenderung kode berkualitas buruk.
Tulains Córdova
1

Saya pikir menggunakan objek yang bisa berubah berasal dari pemikiran imperatif: Anda menghitung hasilnya dengan mengubah konten variabel yang dapat berubah langkah demi langkah (perhitungan dengan efek samping).

Jika Anda berpikir secara fungsional, Anda ingin memiliki status yang tidak dapat diubah dan mewakili status berikutnya dari suatu sistem dengan menerapkan fungsi-fungsi dan menciptakan nilai-nilai baru dari yang lama.

Pendekatan fungsional bisa lebih bersih dan lebih kuat, tetapi bisa sangat tidak efisien karena penyalinan, sehingga Anda ingin kembali ke struktur data bersama yang Anda modifikasi secara bertahap.

Trade-off yang menurut saya paling masuk akal adalah: Mulailah dengan objek yang tidak dapat diubah dan kemudian beralih ke objek yang dapat berubah jika implementasi Anda tidak cukup cepat. Dari sudut pandang ini, menggunakan objek yang bisa berubah secara sistematis dari awal dapat dianggap semacam optimasi prematur : Anda memilih implementasi yang lebih efisien (tetapi juga lebih sulit untuk dipahami dan debug) dari awal.

Jadi, mengapa banyak programmer menggunakan objek yang bisa berubah? IMHO karena dua alasan:

  1. Banyak programmer telah belajar bagaimana memprogram menggunakan paradigma imperatif (berorientasi prosedural atau objek), oleh karena itu mutabilitas adalah pendekatan dasar mereka untuk mendefinisikan komputasi, yaitu mereka tidak tahu kapan dan bagaimana menggunakan immutability karena mereka tidak mengenalnya.
  2. Banyak programmer khawatir tentang kinerja terlalu dini sedangkan seringkali lebih efektif untuk pertama-tama fokus pada penulisan program yang secara fungsional benar, dan kemudian mencoba untuk menemukan hambatan dan mengoptimalkannya.
Giorgio
sumber
1
Masalahnya bukan hanya kecepatan. Ada banyak jenis konstruksi yang berguna yang tidak bisa diimplementasikan tanpa menggunakan tipe yang bisa diubah. Antara lain, komunikasi antara dua utas mensyaratkan bahwa, paling tidak mutlak, keduanya harus memiliki referensi ke objek bersama yang satu dapat menyimpan data dan yang lain dapat membacanya. Lebih jauh, seringkali secara semantik jauh lebih jelas untuk mengatakan "Ubah properti P dan Q dari objek ini" daripada mengatakan "Ambil objek ini, buat objek baru yang sama seperti itu kecuali nilai P, dan kemudian objek baru yang hanya seperti itu kecuali untuk Q ".
supercat
1
Saya bukan pakar FP tetapi AFAIK (1) utas komunikasi dapat dicapai dengan membuat nilai yang tidak dapat diubah dalam satu utas dan membacanya di yang lain (sekali lagi, AFAIK, ini adalah pendekatan Erlang) (2) AFAIK notasi untuk menetapkan properti tidak banyak berubah (mis. di Haskell nilai catatan setProperty sesuai dengan Java record.setProperty (nilai)), hanya perubahan semantik karena hasil pengaturan properti adalah catatan abadi yang baru.
Giorgio
1
"keduanya harus memiliki referensi ke objek bersama yang satu dapat menyimpan data dan yang lainnya dapat membacanya": lebih tepatnya, Anda dapat membuat objek tidak berubah (semua anggota const di C ++ atau final di Jawa) dan mengatur semua konten di konstruktor. Kemudian objek abadi ini diserahkan dari utas produsen ke utas konsumen.
Giorgio
1
(2) Saya sudah mencantumkan kinerja sebagai alasan untuk tidak menggunakan status tidak berubah. Mengenai (1), tentu saja mekanisme yang mengimplementasikan komunikasi antara utas memerlukan keadaan yang bisa berubah, tetapi Anda dapat menyembunyikan ini dari model pemrograman Anda, lihat misalnya model aktor ( en.wikipedia.org/wiki/Actor_model ). Jadi, bahkan jika pada level yang lebih rendah Anda memerlukan kemampuan untuk mengimplementasikan komunikasi, Anda dapat memiliki level abstraksi atas di mana Anda berkomunikasi di antara utas yang mengirim objek yang tidak dapat diubah bolak-balik.
Giorgio
1
Saya tidak yakin saya mengerti Anda sepenuhnya, tetapi bahkan bahasa fungsional murni di mana setiap nilai tidak berubah, ada satu hal yang bisa berubah: status program, yaitu tumpukan program saat ini dan pengikatan variabel. Tapi ini lingkungan eksekusi. Ngomong-ngomong, saya menyarankan agar kita membahas hal ini dalam obrolan beberapa saat dan kemudian membersihkan pesan dari pertanyaan ini.
Giorgio
1

Saya tahu Anda bertanya tentang Java, tapi saya menggunakan bisa berubah vs tidak berubah sepanjang waktu di tujuan-c. Ada larik NSArray yang tidak berubah, dan larik NSMutableArray yang bisa berubah. Ini adalah dua kelas berbeda yang ditulis secara khusus dengan cara yang dioptimalkan untuk menangani penggunaan yang tepat. Jika saya perlu membuat array dan tidak pernah mengubah isinya, saya akan menggunakan NSArray, yang merupakan objek yang lebih kecil dan jauh lebih cepat dalam fungsinya, dibandingkan dengan array yang bisa berubah.

Jadi jika Anda membuat objek Person yang tidak dapat diubah maka Anda hanya perlu konstruktor dan getter, sehingga objek tersebut akan lebih kecil dan menggunakan lebih sedikit memori, yang pada gilirannya akan membuat program Anda sebenarnya lebih cepat. Jika Anda perlu mengubah objek setelah pembuatan, maka objek Person yang dapat berubah akan lebih baik sehingga dapat mengubah nilai alih-alih membuat objek baru.

Jadi: tergantung pada apa yang Anda rencanakan untuk dilakukan dengan objek, memilih bisa berubah vs tidak berubah dapat membuat perbedaan besar, ketika datang ke kinerja.

Michael Ozeryansky
sumber
1

Tambahan untuk banyak alasan lain yang diberikan di sini, masalahnya adalah bahwa bahasa utama tidak mendukung immutabilitas dengan baik. Paling tidak, Anda dihukum karena tidak dapat diubah karena Anda harus menambahkan kata kunci tambahan seperti const atau final, dan harus menggunakan konstruktor yang tidak terbaca dengan banyak, banyak argumen atau kode pola pembangun yang panjang.

Itu jauh lebih mudah dalam bahasa-bahasa yang dibuat dengan ketidakberimbangan dalam pikiran. Pertimbangkan potongan Scala ini untuk mendefinisikan Orang kelas, opsional dengan argumen bernama, dan membuat salinan dengan atribut yang diubah:

case class Person(id: String, firstName: String, lastName: String)

val joe = Person("123", "Joe", "Doe")
val spouse = Person(id = "124", firstName = "Mary", lastName = "Moe")
val joeMarried = joe.copy(lastName = "Doe-Moe")

Jadi, jika Anda ingin pengembang mengadopsi imutabilitas, ini adalah salah satu alasan mengapa Anda mungkin mempertimbangkan untuk beralih ke bahasa pemrograman yang lebih modern.

hstoerr
sumber
ini sepertinya tidak menambah sesuatu yang substansial pada poin yang dibuat dan dijelaskan dalam 24 jawaban sebelumnya
agas
@gnat Manakah dari jawaban sebelumnya yang menunjukkan bahwa sebagian besar bahasa arus utama tidak mendukung ketidakberubahan? Saya pikir titik itu belum dibuat (saya periksa), tapi ini IMHO kendala yang cukup penting.
Hans-Peter Störr
yang satu ini misalnya menjelaskan secara mendalam masalah ini di Jawa. Dan setidaknya 3 jawaban lain berhubungan secara tidak langsung dengannya
agas
0

Tidak Berubah berarti Anda tidak dapat mengubah nilai, dan bisa berubah berarti Anda dapat mengubah nilai jika Anda berpikir dalam hal primitif, dan objek. Objek berbeda dari primitif di Jawa dalam bentuk objek. Primitif dibangun dalam tipe seperti int, boolean, dan void.

Banyak orang berpikir bahwa variabel primitif dan objek yang memiliki pengubah akhir di depannya tidak dapat diubah, namun, ini tidak sepenuhnya benar. Jadi akhir hampir tidak berarti tidak berubah untuk variabel. Lihat tautan ini untuk contoh kode:

public abstract class FinalBase {

    private final int variable; // Unset

    /* if final really means immutable than
     * I shouldn't be able to set the variable
     * but I can.
     */
    public FinalBase(int variable) { 
        this.variable = variable;
    }

    public int getVariable() {
        return variable;
    }

    public abstract void method();
}

// This is not fully necessary for this example
// but helps you see how to set the final value 
// in a sub class.
public class FinalSubclass extends FinalBase {

    public FinalSubclass(int variable) {
        super(variable);
    }

    @Override
    public void method() {
        System.out.println( getVariable() );
    }

    @Override
    public int getVariable() {

        return super.getVariable();
    }

    public static void main(String[] args) {
        FinalSubclass subclass = new FinalSubclass(10);
        subclass.method();
    }
}
agas
sumber
-1

Saya pikir alasan yang bagus adalah untuk memodelkan "benda" nyata "yang bisa berubah", seperti jendela antarmuka. Samar-samar saya ingat pernah membaca bahwa OOP ditemukan ketika seseorang mencoba menulis perangkat lunak untuk mengendalikan beberapa operasi pelabuhan kargo.

Alexey
sumber
1
Saya belum dapat menemukan di mana saya telah membaca ini tentang asal-usul OOP, tetapi menurut Wikipedia , beberapa Sistem Informasi Regional Terpadu dari perusahaan pengiriman kontainer besar OOCL ditulis dalam Smalltalk.
Alexey
-2

Java, dalam banyak kasus mandat objek yang dapat diubah, misalnya ketika Anda ingin menghitung atau mengembalikan sesuatu di pengunjung atau runnable, Anda memerlukan variabel akhir, bahkan tanpa multithreading.

Ralf H
sumber
5
finalsebenarnya sekitar 1/4 langkah menuju keabadian. Tidak melihat mengapa Anda menyebutkannya.
cHao
apakah Anda benar-benar membaca posting saya?
Ralf H
Apakah Anda benar-benar membaca pertanyaannya? :) Itu sama sekali tidak ada hubungannya dengan final. Membesarkannya dalam konteks ini memunculkan penggabungan yang sangat aneh finaldengan "bisa berubah", ketika intinya finaladalah untuk mencegah jenis mutasi tertentu. (BTW, bukan downvote saya.)
cHao
Saya tidak mengatakan Anda tidak memiliki poin valid di sini di suatu tempat. Saya mengatakan Anda tidak menjelaskannya dengan baik. Saya agak melihat di mana Anda mungkin pergi dengan itu, tetapi Anda harus melangkah lebih jauh. Seperti, itu hanya terlihat bingung.
cao
1
Sebenarnya ada sangat sedikit kasus di mana objek bisa berubah diperlukan . Ada beberapa kasus di mana kode akan lebih jelas, atau dalam beberapa kasus lebih cepat, dengan menggunakan objek yang bisa berubah. Tetapi pertimbangkan bahwa sebagian besar pola desain pada dasarnya hanya rendering pemrograman fungsional setengah bahasa untuk bahasa yang tidak mendukungnya. Yang menyenangkan, FP hanya membutuhkan kemampuan berubah-ubah di tempat yang sangat dipilih (yaitu, di mana efek samping harus terjadi), dan tempat-tempat itu umumnya sangat terbatas dalam jumlah, ukuran, dan ruang lingkup.
cao