Bisakah nilai konstanta diubah seiring waktu?

28

Selama fase pengembangan, ada variabel tertentu yang perlu diperbaiki dalam menjalankan yang sama, tetapi mungkin perlu dimodifikasi dari waktu ke waktu. Misalnya a booleanke mode debug mode, jadi kami melakukan hal-hal dalam program yang biasanya tidak kami lakukan.

Apakah gaya buruk mengandung nilai-nilai ini dalam konstanta, yaitu final static int CONSTANT = 0di Jawa? Saya tahu bahwa konstanta tetap sama selama waktu berjalan, tetapi apakah itu juga seharusnya sama selama pengembangan keseluruhan, kecuali untuk perubahan yang tidak direncanakan, tentu saja?

Saya mencari pertanyaan serupa, tetapi tidak menemukan apa pun yang cocok dengan saya.

GregT
sumber
11
Saya ingin tahu, mengapa Anda percaya itu adalah gaya yang buruk untuk mengubahnya?
Vincent Savard
36
Kecuali jika Anda memodelkan properti fisik dengan konstanta yang memiliki nilai matematika, semuanya dapat berubah pada suatu waktu.
Berin Loritsch
19
perangkat lunaknya lunak .
Erik Eidt
10
@ Greg saya tidak setuju. finalmemberi Anda jaminan yang diberlakukan kompiler bahwa program tidak akan mengubah nilai. Saya tidak akan membuang itu hanya karena programmer mungkin ingin memodifikasi nilai yang ditetapkan dalam kode sumber.
Alexander - Pasang kembali Monica
10
Tidak banyak waktu untuk mengartikulasikan jawaban lengkap, tetapi saya menduga kekhawatiran rekan Anda tidak begitu banyak dengan konstanta, tetapi dengan kode-inlining nilai konfigurasi, yang mungkin cenderung bermanifestasi sebagai konstanta. ... Konstanta melindungi Anda dari kesalahan bodoh, seperti menugaskan secara tidak sengaja pada gravitypertengahan pertandingan / lari. Mereka tidak selalu berarti, gravitysama di setiap planet ... Yang mengatakan, solusi yang sehat adalah dengan membuat gravitykonstanta, tetapi menariknya dari planetfile atau database pada awal ruang lingkup yang relevan.
svidgen

Jawaban:

6

Di Jawa, konstanta akhir statis dapat disalin, oleh kompiler, sebagai nilainya, ke dalam kode yang menggunakannya . Sebagai akibatnya, jika Anda merilis versi baru dari kode Anda, dan ada beberapa dependensi hilir yang telah menggunakan konstanta, konstanta dalam kode itu tidak akan diperbarui kecuali kode downstream dikompilasi ulang. Ini bisa menjadi masalah jika mereka menggunakan konstanta itu dengan kode yang mengharapkan nilai baru, meskipun kode sumbernya benar, kode binernya tidak.

Ini adalah kutil dalam desain Java, karena ini adalah salah satu dari sedikit kasus (mungkin satu-satunya kasus) di mana kompatibilitas sumber dan kompatibilitas biner tidak sama. Kecuali untuk kasus ini, Anda dapat menukar ketergantungan dengan versi API-kompatibel baru tanpa pengguna ketergantungan harus melakukan kompilasi ulang. Jelas ini sangat penting mengingat cara di mana dependensi Java umumnya dikelola.

Yang memperburuk masalah adalah bahwa kode hanya akan diam-diam melakukan hal yang salah daripada menghasilkan kesalahan yang bermanfaat. Jika Anda mengganti ketergantungan dengan versi dengan definisi metode atau kelas yang tidak kompatibel, Anda akan mendapatkan kesalahan classloader atau doa, yang setidaknya memberikan petunjuk yang baik tentang apa masalahnya. Kecuali Anda telah mengubah jenis nilai, masalah ini hanya akan muncul sebagai perilaku runtime misterius.

Lebih menjengkelkan adalah bahwa JVM hari ini dapat dengan mudah inline semua konstanta pada saat runtime tanpa penalti kinerja (selain dari kebutuhan untuk memuat kelas mendefinisikan konstanta, yang mungkin sedang dimuat pula), sayangnya semantik tanggal bahasa dari hari-hari sebelum JIT . Dan mereka tidak dapat mengubah bahasa karena kode yang dikompilasi dengan kompiler sebelumnya tidak akan benar. Kompatibilitas bugward menyerang lagi.

Karena semua ini beberapa orang menyarankan untuk tidak mengubah nilai akhir statis sama sekali. Untuk perpustakaan yang mungkin didistribusikan secara luas dan diperbarui dengan cara yang tidak diketahui pada waktu yang tidak diketahui, ini adalah praktik yang baik.

Dalam kode Anda sendiri, terutama di bagian atas hierarki dependensi, Anda mungkin akan lolos begitu saja. Tetapi dalam kasus ini, pertimbangkan apakah Anda benar-benar membutuhkan konstanta untuk publik (atau dilindungi). Jika konstanta hanya visibilitas paket, itu wajar, tergantung pada keadaan dan standar kode Anda, bahwa seluruh paket akan selalu dikompilasi ulang sekaligus, dan masalahnya kemudian hilang. Jika konstanta bersifat pribadi, Anda tidak memiliki masalah dan dapat mengubahnya kapan pun Anda mau.

bulu-bulu
sumber
85

Apa pun dalam kode sumber Anda, termasuk constkonstanta global yang dinyatakan, dapat berubah sewaktu-waktu dengan rilis baru perangkat lunak Anda.

Kata kunci const(atau finaldi Jawa) ada di sana untuk memberi sinyal kepada kompiler bahwa variabel ini tidak akan berubah ketika program ini sedang berjalan . Tidak ada lagi. Jika Anda ingin mengirim pesan ke pengelola berikutnya, gunakan komentar dalam sumber, untuk itulah mereka ada.

// DO NOT CHANGE without consulting with the legal department!
// Get written consent form from them before release!
public const int LegalLimitInSeconds = ...

Merupakan cara yang lebih baik untuk berkomunikasi dengan diri masa depan Anda.

tidak ada
sumber
11
Saya sangat suka komentar ini untuk diri masa depan.
Greg,
4
TaxRatemenjadi publicmembuatku gugup. Saya ingin tahu pasti bahwa hanya departemen penjualan yang terpengaruh oleh perubahan ini dan bukan juga pemasok kami yang mengenakan pajak kepada kami. Siapa yang tahu apa yang terjadi di basis kode sejak komentar itu ditulis.
candied_orange
3
@IllusiveBrian tidak mengkritik penggunaan konstanta. Apakah peringatan terhadap mempercayai komentar akan diperbarui. Selalu pastikan bagaimana sesuatu digunakan sebelum Anda mengubahnya.
candied_orange
8
Ini saran yang bagus untuk Java . Mungkin berbeda dalam bahasa lain. Misalnya, karena cara nilai const terikat ke situs panggilan di C #, public constbidang hanya boleh digunakan untuk hal-hal yang tidak akan pernah berubah, seperti Math.pi. Jika Anda membuat perpustakaan, hal-hal yang mungkin berubah selama pengembangan atau dengan versi baru seharusnya public static readonly, sehingga tidak menyebabkan masalah dengan pengguna perpustakaan Anda.
GrandOpener
6
Anda harus memilih contoh yang berbeda ... nilai uang seharusnya tidak pernah menjadi poin mengambang!
corsiKa
13

Kita perlu membedakan dua aspek konstanta:

  • nama untuk nilai yang dikenal pada waktu pengembangan, yang kami perkenalkan untuk pemeliharaan yang lebih baik, dan
  • nilai-nilai yang tersedia untuk kompiler.

Dan kemudian ada jenis ketiga terkait: variabel yang nilainya tidak berubah, yaitu nama untuk nilai. Perbedaan antara variabel yang tidak berubah ini dan konstanta adalah ketika nilai ditentukan / ditugaskan / diinisialisasi: variabel diinisialisasi pada saat runtime, tetapi nilai konstanta diketahui selama pengembangan. Perbedaan ini agak berlumpur karena nilai dapat diketahui selama pengembangan tetapi sebenarnya hanya dibuat selama inisialisasi.

Tetapi jika nilai konstanta diketahui pada waktu kompilasi, maka kompiler dapat melakukan perhitungan dengan nilai itu. Sebagai contoh, bahasa Jawa memiliki konsep ekspresi konstan . Ekspresi konstan adalah ekspresi apa pun yang hanya terdiri dari literal primitif atau string, operasi pada ekspresi konstan (seperti casting, penambahan, penggabungan string), dan variabel konstan. [ JLS §15.28 ] Variabel konstan adalah finalvariabel yang diinisialisasi dengan ekspresi konstan. [JLS §4.12.4] Jadi untuk Java, ini adalah konstanta waktu kompilasi:

public static final int X = 7;

Ini menjadi menarik ketika variabel konstan digunakan dalam beberapa unit kompilasi, dan kemudian deklarasi diubah. Mempertimbangkan:

  • A.java:

    public class A { public static final int X = 7; }
  • B.java:

    public class B { public static final int Y = A.X + 2; }

Sekarang ketika kita mengkompilasi file-file ini B.classbytecode akan mendeklarasikan sebuah field Y = 9karena B.Ymerupakan variabel konstan.

Tetapi ketika kita mengubah A.Xvariabel ke nilai yang berbeda (katakanlah X = 0) dan hanya mengkompilasi ulang A.javafile, maka B.Ymasih mengacu pada nilai yang lama. Keadaan A.X = 0, B.Y = 9ini tidak konsisten dengan deklarasi dalam kode sumber. Selamat men-debug!

Ini tidak berarti bahwa konstanta tidak boleh diubah. Konstanta secara definitif lebih baik daripada angka ajaib yang muncul tanpa penjelasan dalam kode sumber. Namun, yang nilai dari konstanta publik merupakan bagian dari API publik Anda . Ini tidak khusus untuk Java, tetapi juga terjadi di C ++ dan bahasa lain yang menampilkan unit kompilasi terpisah. Jika Anda mengubah nilai-nilai ini, Anda harus mengkompilasi ulang semua kode dependen, yaitu melakukan kompilasi bersih.

Tergantung pada sifat konstanta, mereka mungkin menyebabkan asumsi yang salah oleh pengembang. Jika nilai-nilai ini diubah, mereka dapat memicu bug. Sebagai contoh, satu set konstanta dapat dipilih sehingga mereka membentuk pola bit tertentu, misalnya public static final int R = 4, W = 2, X = 1. Jika ini diubah untuk membentuk struktur yang berbeda seperti R = 0, W = 1, X = 2maka kode yang ada seperti boolean canRead = perms & Rmenjadi salah. Dan pikirkan kesenangan yang akan terjadi kemudian Integer.MAX_VALUEberubah! Tidak ada perbaikan di sini, hanya penting untuk diingat bahwa nilai beberapa konstanta benar-benar penting dan tidak dapat diubah secara sederhana.

Tetapi untuk mayoritas konstanta yang mengubahnya akan baik-baik saja selama pembatasan di atas dipertimbangkan. Konstanta aman untuk diubah ketika maknanya, bukan nilai spesifik yang penting. Ini adalah contoh kasus untuk merdu seperti BORDER_WIDTH = 2atau TIMEOUT = 60; // secondsatau template seperti API_ENDPOINT = "https://api.example.com/v2/"- meskipun bisa dibilang beberapa atau semua dari mereka harus ditentukan dalam file konfigurasi daripada kode.

amon
sumber
5
Saya suka analisis ini. Saya membacanya sebagai: Anda bebas untuk mengubah konstanta selama Anda memahami bagaimana itu digunakan.
candied_orange
+1 C # juga "menderita" dari masalah yang sama dengan konstanta publik.
Reginald Blue
6

Konstanta hanya dijamin konstan untuk umur runtime aplikasi . Selama itu benar, tidak ada alasan untuk tidak memanfaatkan fitur bahasa. Anda hanya perlu tahu apa konsekuensinya untuk menggunakan flag konstan vs compiler untuk tujuan yang sama:

  • Konstanta mengambil ruang aplikasi
  • Bendera kompiler tidak
  • Kode yang dimatikan oleh konstanta dapat diperbarui dan diubah dengan alat refactoring modern
  • Kode dimatikan oleh flag compiler tidak bisa

Setelah mempertahankan aplikasi yang akan mengaktifkan menggambar kotak pembatas untuk bentuk sehingga kami bisa men-debug bagaimana mereka digambar, kami mengalami masalah. Setelah refactoring, semua kode yang dimatikan oleh flag compiler tidak akan dikompilasi. Setelah itu, kami sengaja mengubah flag compiler kami menjadi konstanta aplikasi.

Saya mengatakan itu untuk menunjukkan bahwa ada pertukaran. Bobot beberapa booleans tidak akan membuat aplikasi kehabisan memori atau mengambil terlalu banyak ruang. Itu mungkin tidak benar jika konstanta Anda benar-benar objek besar yang pada dasarnya memiliki pegangan untuk semua yang ada dalam kode Anda. Jika tidak hati-hati menghapus semua referensi yang dipegangnya pada suatu objek setelah tidak diperlukan lagi, maka objek Anda mungkin menjadi sumber kebocoran memori.

Anda perlu mengevaluasi kasus penggunaan dan mengapa Anda ingin mengubah konstanta.

Saya bukan penggemar pernyataan selimut sederhana, tetapi secara umum kolega senior Anda benar. Jika sesuatu cenderung berubah sering, itu mungkin harus menjadi item yang dapat dikonfigurasi. Misalnya, Anda mungkin bisa meyakinkan kolega Anda untuk konstanta IsInDebugMode = trueketika Anda ingin melindungi beberapa kode dari kerusakan. Namun, beberapa hal mungkin perlu diubah lebih sering daripada Anda merilis aplikasi. Jika demikian, Anda perlu cara mengubah nilai itu pada waktu yang tepat. Anda dapat mengambil contoh a TaxRate = .065. Itu mungkin benar pada saat Anda mengkompilasi kode Anda, tetapi karena undang-undang baru itu dapat berubah sebelum Anda merilis versi berikutnya dari aplikasi Anda. Itu adalah sesuatu yang perlu diperbarui baik dari beberapa mekanisme penyimpanan (seperti file atau database)

Berin Loritsch
sumber
Apa yang Anda maksud dengan “flag compiler”? Mungkin preprocessor C dan fitur kompiler serupa yang mendukung makro / definisi dan #ifdefs? Karena ini didasarkan pada substitusi teks dari kode sumber, mereka bukan bagian dari semantik bahasa pemrograman. Perhatikan bahwa Java tidak memiliki preprosesor.
Amon
@amon, Java mungkin tidak, tetapi beberapa bahasa bisa. Maksud saya #ifdefbendera. Meskipun mereka bukan bagian dari semantik C, mereka adalah bagian dari C #. Saya menulis untuk konteks agnostisisme bahasa yang lebih besar.
Berin Loritsch
Saya pikir argumen "pemborosan memori" sedang diperdebatkan. In-lining dan pengocokan pohon adalah langkah universal yang cukup banyak di setiap optimizer mode rilis.
Alexander - Reinstate Monica
@Alexander, saya setuju. Ini adalah sesuatu yang harus diperhatikan.
Berin Loritsch
1
"Konstanta mengambil ruang aplikasi" - kecuali jika Anda mengembangkan aplikasi tertanam untuk mikrokontroler dengan hanya satu atau dua kilobyte memori, Anda bahkan tidak boleh memikirkan hal-hal seperti itu.
vsz
2

Petunjuk const, #defineatau finalpetunjuk kompiler (perhatikan bahwa #definesebenarnya bukan petunjuk, ini adalah petunjuk makro dan jauh lebih kuat). Ini menunjukkan bahwa nilai tidak akan berubah selama pelaksanaan suatu program dan berbagai optimasi dapat dilakukan.

Namun, sebagai petunjuk kompiler, kompiler melakukan hal-hal yang mungkin tidak selalu diharapkan oleh programmer. Secara khusus, javac akan inline static final int FOO = 42;sehingga di mana saja yang FOOdigunakan, sebenarnya dikompilasi kode byte akan membaca 42.

Ini tidak terlalu mengejutkan sampai seseorang mengubah nilainya tanpa mengkompilasi ulang unit kompilasi lainnya (file .java) - dan 42sisa - sisa dalam kode byte (lihat apakah mungkin untuk menonaktifkan inlining javac tentang variabel akhir statis? ).

Membuat sesuatu static finalberarti bahwa itu adalah itu dan selamanya lebih dari itu dan mengubahnya itu adalah Kesepakatan yang Sangat Besar - terutama jika itu bukan apa-apa private.

Konstanta untuk hal-hal seperti final static int ZERO = 0bukan masalah. final static double TAX_RATE = 0.55(Selain karena uang dan dobel itu buruk dan harus menggunakan BigDecimal, tapi itu bukan primitif dan karenanya tidak diuraikan) adalah masalah dan harus diperiksa dengan hati-hati untuk penggunaannya.

pengguna292808
sumber
untuk nilai kecil NOL.
3
is a problem and should be examined with great care for where it is used.Mengapa ini menjadi masalah?
Alexander - Reinstate Monica
1

Seperti namanya, konstanta tidak boleh berubah selama runtime dan menurut saya konstanta didefinisikan tidak berubah untuk jangka panjang (Anda dapat melihat pertanyaan SO ini untuk informasi lebih lanjut.

Ketika datang ke kebutuhan flag (misalnya untuk mode pengembangan) Anda harus menggunakan file konfigurasi atau parameter startup (banyak IDE mendukung konfigurasi parameter startup pada per-proyek-basis; lihat dokumentasi yang relevan) untuk mengaktifkan mode ini - dengan cara ini Anda menjaga fleksibilitas untuk menggunakan mode seperti itu dan Anda tidak bisa lupa untuk mengubahnya setiap kali kode menjadi produktif.

DMuenstermann
sumber
0

Mampu diubah antara proses adalah salah satu poin paling penting dalam mendefinisikan konstanta dalam kode sumber Anda!

Konstanta memberi Anda lokasi yang terdefinisi dengan baik dan terdokumentasi (dalam arti tertentu) untuk mengubah nilai kapan pun Anda perlu selama masa hidup kode sumber Anda. Ini juga merupakan janji bahwa mengubah konstanta di lokasi ini akan benar-benar mengubah semua kejadian apa pun artinya.

Sebagai contoh yang merugikan: tidak masuk akal untuk memiliki konstanta TRUEyang dievaluasi truedalam bahasa yang benar-benar memiliki truekata kunci. Anda tidak akan pernah, tidak pernah sekalipun, menyatakan TRUE=falsekecuali sebagai lelucon kejam.

Tentu saja ada kegunaan lain dari konstanta, misalnya kode shortening ( CO_NAME = 'My Great World Unique ACME Company'), menghindari duplikasi ( PI=3.141), pengaturan konvensi ( TRUE=1) atau apa pun, tetapi memiliki posisi yang ditentukan untuk mengubah konstanta tentu saja merupakan salah satu yang paling menonjol.

AnoE
sumber