Di mana Anda harus meletakkan konstanta dan mengapa?

34

Dalam sebagian besar aplikasi besar kami, kami biasanya hanya memiliki beberapa lokasi untuk "konstanta":

  • Satu kelas untuk GUI dan kontestan internal (judul Halaman Tab, judul Kotak Grup, faktor perhitungan, enumerasi)
  • Satu kelas untuk tabel dan kolom basis data (bagian ini menghasilkan kode) plus nama yang dapat dibaca untuk mereka (ditetapkan secara manual)
  • Satu kelas untuk pesan aplikasi (logging, kotak pesan, dll)

Konstanta biasanya dipisahkan menjadi struct yang berbeda di kelas-kelas tersebut. Dalam aplikasi C ++ kami, konstanta hanya didefinisikan dalam file .h dan nilai ditetapkan dalam file .cpp.

Salah satu keuntungannya adalah bahwa semua string dll berada di satu tempat pusat dan semua orang tahu di mana menemukannya ketika sesuatu harus diubah.

Ini terutama sesuatu yang tampaknya disukai manajer proyek ketika orang datang dan pergi dan dengan cara ini semua orang dapat mengubah hal-hal sepele tanpa harus menggali ke dalam struktur aplikasi.

Juga, Anda dapat dengan mudah mengubah judul Kotak Grup / Halaman Tab serupa dll sekaligus. Aspek lain adalah Anda hanya dapat mencetak kelas itu dan memberikannya kepada non-programmer yang dapat memeriksa apakah teksnya intuitif, dan apakah pesan kepada pengguna terlalu rinci atau terlalu membingungkan, dll.

Namun, saya melihat kerugian tertentu:

  • Setiap kelas tunggal sangat erat dengan kelas konstanta
  • Menambah / Menghapus / Mengganti Nama / Memindahkan konstanta membutuhkan kompilasi ulang setidaknya 90% dari aplikasi (Catatan: Mengubah nilai tidak, setidaknya untuk C ++). Dalam salah satu proyek C ++ kami dengan 1500 kelas, ini berarti sekitar 7 menit waktu kompilasi (menggunakan header yang dikompilasi; tanpa mereka itu sekitar 50 menit) ditambah sekitar 10 menit menghubungkan ke perpustakaan statis tertentu.
  • Membangun rilis yang dioptimalkan dengan kecepatan melalui Visual Studio Compiler membutuhkan waktu hingga 3 jam. Saya tidak tahu apakah jumlah besar dari hubungan kelas adalah sumber tetapi mungkin juga begitu.
  • Anda didorong ke string sementara yang langsung dikodekan ke dalam kode karena Anda ingin menguji sesuatu dengan sangat cepat dan tidak ingin menunggu 15 menit hanya untuk tes itu (dan mungkin setiap yang berikutnya). Semua orang tahu apa yang terjadi pada "Saya akan memperbaikinya nanti".
  • Menggunakan kembali kelas di proyek lain tidak selalu mudah (terutama karena kopling ketat lainnya, tetapi penanganan konstanta tidak membuatnya lebih mudah.)

Di mana Anda menyimpan konstanta seperti itu? Juga argumen apa yang akan Anda bawa untuk meyakinkan manajer proyek Anda bahwa ada konsep yang lebih baik yang juga sesuai dengan keuntungan yang tercantum di atas?

Jangan ragu untuk memberikan jawaban C ++ - spesifik atau independen.

PS: Saya tahu pertanyaan ini agak subyektif tapi saya jujur ​​tidak tahu tempat yang lebih baik daripada situs ini untuk pertanyaan semacam ini.

Perbarui proyek ini

Saya punya berita tentang waktu kompilasi:
Mengikuti posting Caleb dan gbjbaanb, saya membagi file konstanta saya menjadi beberapa file lain ketika saya punya waktu. Saya juga akhirnya membagi proyek saya menjadi beberapa perpustakaan yang sekarang mungkin lebih mudah. Kompilasi ini dalam mode rilis menunjukkan bahwa file yang dihasilkan secara otomatis yang berisi definisi basis data (tabel, nama kolom, dan lainnya - lebih dari 8000 simbol) dan membangun hash tertentu menyebabkan waktu kompilasi yang sangat besar dalam mode rilis.

Menonaktifkan pengoptimal MSVC untuk perpustakaan yang berisi konstanta DB sekarang memungkinkan kami untuk mengurangi total waktu kompilasi Proyek Anda (beberapa aplikasi) dalam mode rilis dari hingga 8 jam menjadi kurang dari satu jam!

Kami belum mengetahui mengapa MSVC kesulitan mengoptimalkan file-file ini, tetapi untuk saat ini perubahan ini mengurangi banyak tekanan karena kami tidak lagi harus bergantung hanya pada build malam saja.

Fakta itu - dan manfaat lainnya, seperti kopling yang kurang rapat, penggunaan kembali yang lebih baik, dll. - juga menunjukkan bahwa menghabiskan waktu untuk memisahkan "konstanta" bukanlah ide yang buruk ;-)

Pembaruan2

Karena pertanyaan ini masih mendapat perhatian:
Inilah yang telah saya lakukan dalam beberapa tahun terakhir:

Letakkan setiap konstanta, variabel, dll, persis di dalam cakupan yang relevan untuknya: Jika Anda menggunakan konstanta hanya dalam satu metode, tidak apa-apa untuk mendefinisikannya dalam metode itu. Jika satu kelas tertarik, biarkan itu sebagai detail implementasi pribadi dari kelas itu. Hal yang sama berlaku untuk ruang nama, modul, proyek, ruang lingkup perusahaan. Saya juga menggunakan pola yang sama untuk fungsi pembantu dan sejenisnya. (Ini mungkin tidak berlaku 100% jika Anda mengembangkan kerangka kerja publik.)

Melakukan peningkatan reusabilitas, pengujian, dan pemeliharaan ini hingga pada tingkat di mana Anda tidak hanya menghabiskan lebih sedikit waktu untuk kompilasi (setidaknya dalam C ++), tetapi juga lebih sedikit waktu untuk memperbaiki bug, yang membuat Anda lebih banyak waktu untuk benar-benar mengembangkan fitur baru. Pada saat yang sama, mengembangkan fitur-fitur ini akan berjalan lebih cepat karena Anda dapat menggunakan kembali kode lebih mudah. Ini melebihi keuntungan apa pun yang mungkin dimiliki oleh file konstanta pusat.

Lihatlah terutama Prinsip Segregasi Antarmuka dan Prinsip Tanggung Jawab Tunggal jika Anda ingin tahu lebih banyak.

Jika Anda setuju, pilih jawaban Caleb karena pembaruan ini pada dasarnya lebih bersifat umum tentang apa yang dikatakannya.

Tim Meyer
sumber
2
secara pribadi saya tidak akan memiliki judul UI atau string pesan dalam konstanta sama sekali. Saya akan memilikinya di app.config
jk.
1
Saya agak suka bagaimana Anda melakukannya sekarang - saya mengerti kerugian Anda tetapi kita mungkin hanya harus berurusan dengan itu.
bigtang
1
Saya telah melihat masalah yang sama persis dalam proyek Java yang besar ... Satu antarmuka "konstanta" yang sangat besar, ubah apa saja di dalamnya dan tunggu 15 menit hingga gerhana untuk dikompilasi ulang. Saya bersama Caleb: kelompokkan konstanta tempat mereka berada, dekat dengan kode yang menggunakannya. Menjaga mereka terpisah karena mereka adalah konstanta adalah latihan OCD yang tidak berguna.
Michael Borgwardt
Kopling yang kencang bukan masalah, karena itulah yang sebenarnya Anda inginkan. Anda ingin satu perubahan dalam file konstanta Anda memengaruhi kemungkinan banyak file sumber. (Tentu saja Anda memiliki masalah lain juga).
gnasher729
@ gnasher729 itu hanya benar jika banyak kelas menggunakan konstanta yang sama. Anda tidak akan pernah ingin kelas digabungkan dengan konstanta yang tidak berhubungan dengan mereka. Awalnya mungkin tidak akan menjadi masalah sampai Anda mencoba menggunakannya kembali di proyek lain tanpa menyalinnya atau menjalankan tes yang terisolasi
Tim Meyer

Jawaban:

29

Konstanta yang khusus untuk kelas harus masuk dalam antarmuka kelas itu.

Konstanta yang benar-benar opsi konfigurasi harus menjadi bagian dari kelas konfigurasi. Jika Anda menyediakan accessor untuk opsi konfigurasi di kelas itu (dan menggunakannya sebagai pengganti konstanta di tempat lain), Anda tidak perlu mengkompilasi ulang seluruh dunia ketika Anda mengubah beberapa opsi.

Konstanta yang dibagi antara kelas tetapi yang tidak dimaksudkan untuk dapat dikonfigurasi harus dicakup secara wajar - cobalah untuk memecahnya menjadi file yang memiliki kegunaan khusus sehingga kelas individu hanya menyertakan apa yang sebenarnya mereka butuhkan. Ini lagi akan membantu mengurangi waktu kompilasi ketika Anda mengubah beberapa konstanta itu.

Caleb
sumber
1
Jika Anda tertarik: Saya memperbarui pertanyaan saya dengan apa yang telah saya capai, mengikuti jawaban Anda dan yang lainnya
Tim Meyer
6

Saya katakan secara sederhana bahwa Anda ingin membagi kelas konstanta besar Anda menjadi banyak file kecil, misalnya satu per bentuk. Ini memastikan Anda tidak memiliki ketergantungan yang begitu besar pada file konstanta, jadi menambahkan atau memperbarui string tidak akan memerlukan kompilasi ulang total. Anda masih dapat menyimpan file-file ini di lokasi pusat, tetapi (misalnya) memiliki 1 file dengan konstanta untuk setiap dialog, sesuai namanya. Maka Anda hanya dapat memasukkan file-file itu dalam file dialog yang relevan, yang mengurangi kompilasi secara besar-besaran.

Saya juga menyarankan Anda menggunakan sesuatu seperti utilitas GNU GetText untuk menangani string, itu dirancang untuk terjemahan, tetapi berfungsi dengan baik untuk hanya mengubah teks ke sesuatu yang lain. Anda dapat menempatkan mereka di sumber daya string, tetapi saya menemukan mereka menjadi lebih sulit untuk dikerjakan karena mereka dikunci oleh ID, utils GetText dikunci oleh string asli - membuat semuanya sangat mudah untuk dikembangkan.

gbjbaanb
sumber
Saya mungkin mencobanya. Sebagai kelas konstanta dengan judul dll dibagi menjadi beberapa struct, saya mungkin bisa melakukan satu kelas per struct sebagai awal. Tidak yakin tentang masalah GNU, kami biasanya tidak ingin mengubah string kami saat runtime, hanya selama waktu pengembangan. Kami menggunakan mekanisme terjemahan Qt, jika kami harus menerjemahkan ke bahasa lain di masa depan.
Tim Meyer
Jika Anda tertarik: Saya memperbarui pertanyaan saya dengan apa yang telah saya capai, mengikuti jawaban Anda dan yang lain
Tim Meyer
2

Catatan: Saya bukan pengembang C ++ ... tapi inilah pemikiran saya: Anda perlu mempertimbangkan untuk mengikuti komentar @ jk tentang perbedaan antara menggunakan file konfigurasi. Di DotNet, ada file sumber daya yang digunakan untuk menyimpan informasi tersebut. Dalam Formulir Windows, file sumber daya dikelola dari VS untuk setiap formulir.

Saya tidak melihat nilai untuk konstanta ditempatkan di luar ruang lingkup penggunaannya kecuali konstanta global yang harus dibagi. Seperti yang Anda sebutkan ini akan sulit untuk mempertahankan setidaknya selama pengembangan. Juga, Anda mungkin mendapatkan bentrokan nama. Hal lain adalah bahwa mungkin sulit untuk mengetahui siapa yang menggunakan konstanta yang diberikan.

Sekarang jika Anda ingin non-programmer untuk meninjau informasi, maka untuk GUI, Anda menangkap layar untuk mereka. Jika Anda ingin mereka meninjau entri tabel data, Anda bisa mengekspor data ke Excel atau yang serupa.

Jika Anda masih ingin pergi dengan pendekatan lokasi terpusat dan Anda ingin menempatkan semua konstanta Anda dalam satu file besar, setiap pengembang dapat menggunakan file bersama yang diperbarui pada akhir setiap interval ke file pusat. Data akan berasal dari file individual yang digunakan dalam pengembangan. Ini dapat dengan mudah diotomatisasi atau dilakukan secara manual. Namun, seperti yang saya katakan itu mungkin risiko yang tidak harus Anda ambil.

Tidak ada kesempatan
sumber
2

Tidak ada solusi umum. Tanyakan pada diri sendiri tentang kinerja, kegunaan, keamanan, dan siklus hidup yang konstan.

Semakin dekat mereka dengan ruang lingkup mereka semakin tinggi kinerja.

Semakin mereka dikelompokkan secara logis dan di luar ruang lingkup mereka, semakin tinggi reusability.

Semakin sedikit aksesibilitas, semakin tinggi keamanan.

Semakin tinggi masa pakai sebuah konstanta, semakin sedikit peduli tempat Anda menggunakannya.

Konstanta seperti nomor versi akan didefinisikan dalam beberapa jenis manifes. Kode kesalahan fungsi kesalahan akan didefinisikan di dalam kelas. Kode kesalahan kemungkinan adalah sesuatu dengan masa hidup yang tinggi (= hampir tidak pernah berubah). Menempatkannya di file konstan hanya akan meng-spam file dengan hal-hal yang tidak perlu.

Semakin sedikit konstanta memiliki karakter konstanta tetapi variabel (seperti nomor versi) semakin Anda dapat meletakkannya di luar. Semakin sedikit variabel konstanta, maka semakin konstan, semakin harus ditempatkan di dalam cakupannya. Selama debugging, masuk akal untuk meletakkannya di luar untuk mengurangi waktu kompilasi.

Namun, masalah awal Anda adalah waktu kompilasi. Jadi pertanyaannya adalah apakah Anda mengajukan pertanyaan yang tepat. Jika waktu kompilasi aplikasi Anda terlalu tinggi, Anda sebaiknya memikirkan cara untuk membuatnya lebih modular sehingga bagian-bagiannya bekerja secara independen satu sama lain. Kompilasi sebagian dan uji barang Anda secara independen. Jika unit test Anda dilakukan dengan benar dan full-blown (yang sebenarnya merupakan banyak pekerjaan), maka Anda dapat dengan mudah memindahkan barang tanpa harus khawatir tentang hal itu. Dan kemudian pertanyaannya mendapatkan drive yang sama sekali berbeda.

Kristen
sumber
1

Saya akan menyarankan menempatkan semua konstanta ini ke dalam semacam file konfigurasi. Untuk aplikasi Java, kami biasanya menggunakan file .properties, teks sederhana dengan setiap baris diformat sebagai "(kunci) = (nilai)". Contoh

MainPanel.Title = Selamat datang di aplikasi kami
DB.table.users = TBL_USERS
logging.filename = application.log

Anda kemudian memuat file ini saat runtime, mengisi cache yang memungkinkan Anda mencari kunci dan mendapatkan kembali nilai. Ketika Anda membutuhkan konstanta, Anda kemudian meminta cache. Anda masih perlu untuk memiliki kunci Anda di suatu tempat, dan cache akan perlu diakses secara global, tetapi ketika Anda mengubah sebenarnya nilai dari konstanta, tidak ada kompilasi ulang diperlukan, itu harus mungkin untuk hanya memulai kembali aplikasi (atau jika Anda benar-benar ingin mendapatkan yang mewah, memiliki beberapa file .properties dan cache serta memberikan aplikasi kemampuan untuk memuat ulang cache saat run-time).

Untuk implementasi, saya menemukan pertanyaan SO ini: https://stackoverflow.com/questions/874052/properties-file-library-for-c-or-c (itu adalah hit pertama pada pencarian Google - saya belum sebenarnya menggunakan perangkat lunak ini sendiri).

FrustratedWithFormsDesigner
sumber
Untuk proyek C ++, kita hanya perlu mengkompilasi ulang file konstanta jika kita mengubah nilai. Ini karena nilai diberikan dalam file .cpp. Namun menambahkan / menghapus / mengganti nama / memindahkan konstanta masih membutuhkan pembangunan kembali penuh
Tim Meyer
@ Timimeyer: Jika Anda membagi konstanta ke beberapa file, menambah / menghapus konstanta hanya akan mempengaruhi file yang bergantung pada file tertentu, betul?
FrustratedWithFormsDesigner
Benar. Masalah utama adalah bahwa jika saya menyarankan itu, orang cenderung menyebutkan salah satu hal yang saya cantumkan sebagai "keuntungan" hilang
Tim Meyer
Namun, saya belum benar-benar berpikir tentang meletakkan beberapa file konstanta di tempat yang sama
Tim Meyer
+1. @Tim Meyer: Untuk tujuan ini, biaya pemeriksaan waktu kompilasi jauh lebih banyak daripada menghemat. Selain itu, apa yang akan Anda lakukan jika Anda perlu menginternasionalkan?
kevin cline