Saya sedang menulis sebuah program di Jawa di mana pada satu titik saya perlu memuat kata sandi untuk keystore saya. Hanya untuk bersenang-senang, saya mencoba menjaga kata sandi saya di Jawa sesingkat mungkin dengan melakukan ini:
//Some code
....
KeyManagerFactory keyManager = KeyManagerFactory.getInstance("SunX509");
Keystore keyStore = KeyStore.getInstance("JKS");
{
char[] password = getPassword();
keyStore.load(new FileInputStream(keyStoreLocation), password);
keyManager.init(keyStore, password);
}
...
//Some more code
Sekarang, saya tahu dalam hal ini agak bodoh. Ada banyak hal lain yang bisa saya lakukan, kebanyakan dari mereka sebenarnya lebih baik (saya tidak bisa menggunakan variabel sama sekali).
Namun, saya ingin tahu apakah ada kasus di mana melakukan ini tidak begitu bodoh. Satu-satunya hal lain yang dapat saya pikirkan adalah jika Anda ingin menggunakan kembali nama variabel umum seperti count
, atau temp
, tetapi konvensi penamaan yang baik dan metode singkat membuatnya tidak mungkin berguna.
Apakah ada kasus di mana menggunakan blok hanya untuk mengurangi ruang lingkup variabel masuk akal?
Jawaban:
Pertama, berbicara dengan mekanik yang mendasarinya:
Dalam C ++ scope == Seumur Hidup b / c destructor dipanggil saat keluar dari scope. Selanjutnya, perbedaan penting dalam C / C ++ kita dapat mendeklarasikan objek lokal. Dalam runtime untuk C / C ++ kompiler umumnya akan mengalokasikan bingkai tumpukan untuk metode yang sebesar mungkin diperlukan, di muka, daripada mengalokasikan lebih banyak ruang tumpukan pada entri ke setiap lingkup (yang menyatakan variabel). Jadi, kompiler sedang menciutkan atau meratakan cakupan.
Kompiler C / C ++ dapat menggunakan kembali ruang penyimpanan tumpukan untuk penduduk setempat yang tidak mengalami konflik dalam masa penggunaan (umumnya akan menggunakan analisis kode aktual untuk menentukan ini daripada ruang lingkup, b / c yang lebih akurat daripada ruang lingkup!).
Saya menyebutkan C / C ++ b / c sintaksis Java, yaitu kurung kurawal dan pelingkupan, setidaknya sebagian berasal dari keluarga itu. Dan juga karena C ++ muncul dalam komentar pertanyaan.
Sebaliknya, tidak mungkin untuk memiliki objek lokal di Jawa: semua objek adalah objek tumpukan, dan semua lokal / formal adalah variabel referensi atau tipe primitif (btw, hal yang sama berlaku untuk statika).
Selanjutnya, dalam lingkup dan masa hidup Jawa tidak sepenuhnya disamakan: berada di / keluar dari ruang lingkup sebagian besar adalah konsep waktu kompilasi pergi ke aksesibilitas dan konflik nama; tidak ada yang benar-benar terjadi di Java pada exit scope terkait dengan pembersihan variabel. Pengumpulan sampah Jawa menentukan (titik akhir) objek seumur hidup.
Juga mekanisme bytecode Java (output dari Java compiler) cenderung untuk mempromosikan variabel-variabel pengguna yang dideklarasikan dalam lingkup terbatas ke tingkat atas metode, karena tidak ada fitur lingkup pada tingkat bytecode (ini mirip dengan penanganan C / C ++ dari tumpukan). Paling-paling kompiler dapat menggunakan kembali slot variabel lokal, tetapi (tidak seperti C / C ++) hanya jika jenisnya sama.
(Padahal, untuk memastikan compiler JIT yang mendasarinya dalam runtime dapat menggunakan kembali ruang penyimpanan stack yang sama untuk dua penduduk lokal yang diketik berbeda (dan ditempatkan berbeda), dengan analisis yang memadai dari bytecode.)
Berbicara untuk keuntungan programmer, saya akan cenderung setuju dengan yang lain bahwa metode (pribadi) adalah konstruk yang lebih baik bahkan jika digunakan hanya sekali.
Namun, tidak ada yang salah dengan menggunakannya untuk mendekripsi nama.
Saya telah melakukan ini dalam keadaan yang jarang terjadi ketika membuat metode individual adalah beban. Sebagai contoh, ketika menulis penerjemah menggunakan
switch
pernyataan besar dalam satu lingkaran, saya mungkin (tergantung pada faktor-faktor) memperkenalkan blok terpisah untuk masing-masingcase
untuk menjaga masing-masing kasus lebih terpisah satu sama lain, daripada membuat setiap kasus metode yang berbeda (masing-masing dari yang hanya dipanggil sekali).(Catat bahwa sebagai kode dalam blok, masing-masing case memiliki akses ke pernyataan "break;" dan "continue;" mengenai loop penutup, sedangkan sebagai metode akan membutuhkan boolean yang kembali dan penggunaan kondisional pemanggil untuk mendapatkan akses ke aliran kontrol ini pernyataan.)
sumber
{ int x = 42; String s="blah"; } { long y = 0; }
,long
variabel akan menggunakan kembali slot kedua variabel,x
dans
dengan semua kompiler yang biasa digunakan (javac
danecj
).Menurut pendapat saya akan lebih jelas untuk menarik blok ke metode sendiri. Jika Anda meninggalkan blok ini, saya berharap untuk melihat komentar yang menjelaskan mengapa Anda meletakkan blok di tempat pertama. Pada saat itu terasa lebih bersih untuk memiliki pemanggilan metode
initializeKeyManager()
yang membuat variabel kata sandi terbatas pada ruang lingkup metode dan menunjukkan maksud Anda dengan blok kode.sumber
Mereka dapat berguna di Rust, dengan aturan peminjaman yang ketat. Dan umumnya, dalam bahasa fungsional, di mana blok itu mengembalikan nilai. Tetapi dalam bahasa seperti Jawa? Meskipun ide itu tampak elegan, dalam praktiknya jarang digunakan sehingga kebingungan dari melihatnya akan mengecilkan potensi kejelasan membatasi ruang lingkup variabel.
Ada satu kasus di mana saya menemukan ini berguna - dalam sebuah
switch
pernyataan! Terkadang Anda ingin mendeklarasikan variabel dalamcase
:Namun, ini akan gagal:
switch
pernyataan itu aneh - itu adalah pernyataan kontrol, tetapi karena kegagalan mereka, mereka tidak memperkenalkan cakupan.Jadi - Anda bisa menggunakan blok untuk membuat lingkup sendiri:
sumber
Pada umumnya dianggap praktik yang baik untuk membuat metode tetap singkat. Mengurangi ruang lingkup beberapa variabel bisa menjadi tanda bahwa metode Anda cukup panjang, cukup sehingga Anda berpikir ruang lingkup variabel perlu dikurangi. Dalam hal ini, mungkin perlu membuat metode terpisah untuk bagian kode tersebut.
Kasus-kasus yang saya temukan bermasalah adalah ketika bagian kode menggunakan lebih dari beberapa argumen. Ada situasi di mana Anda bisa berakhir dengan metode kecil yang masing-masing mengambil banyak parameter (atau perlu solusi untuk mensimulasikan pengembalian beberapa nilai). Solusi untuk masalah khusus itu adalah membuat kelas lain yang tetap menggunakan berbagai variabel yang Anda gunakan, dan mengimplementasikan mengimplementasikan semua langkah yang ada dalam pikiran Anda dalam metode yang relatif singkat: ini adalah kasus penggunaan khas untuk menggunakan pola Builder .
Yang mengatakan, semua pedoman pengkodean ini kadang-kadang dapat diterapkan secara longgar. Kadang-kadang ada kasus aneh, di mana kode bisa lebih mudah dibaca jika Anda menyimpannya dalam metode yang sedikit lebih lama, daripada membaginya menjadi sub-metode kecil (atau pola pembangun). Biasanya, ini berfungsi untuk masalah yang berukuran sedang, cukup linier, dan di mana terlalu banyak pemisahan sebenarnya menghambat keterbacaan (terutama jika Anda perlu melompat di antara terlalu banyak metode untuk memahami apa yang dilakukan kode).
Ini bisa masuk akal, tetapi sangat jarang.
Ini kodenya:
Anda sedang membuat
FileInputStream
, tetapi Anda tidak pernah menutupnya.Salah satu cara untuk memecahkan masalah ini adalah dengan menggunakan pernyataan coba-dengan-sumber daya . Ini cakupan
InputStream
, dan Anda juga dapat menggunakan blok ini untuk mengurangi cakupan variabel lain jika Anda inginkan (karena alasan, karena baris ini terkait):(Ngomong-ngomong juga sering lebih baik digunakan
getDefaultAlgorithm()
daripadaSunX509
, omong-omong.)Selain itu, sementara saran untuk memisahkan potongan kode menjadi metode yang terpisah umumnya baik. Jarang sepadan jika hanya untuk 3 baris (jika ada, itu adalah kasus di mana membuat metode terpisah menghambat keterbacaan).
sumber
Menurut pendapat saya yang sederhana, itu umumnya sesuatu untuk dihindari sebagian besar untuk kode produksi karena Anda umumnya tidak tergoda untuk melakukannya kecuali dalam fungsi sporadis yang melakukan tugas yang berbeda. Saya cenderung melakukannya dalam beberapa memo kode yang digunakan untuk menguji sesuatu, tetapi tidak menemukan godaan untuk melakukannya dalam kode produksi di mana saya sudah memikirkan terlebih dahulu apa yang harus dilakukan masing-masing fungsi, sejak itu fungsi secara alami akan memiliki sangat ruang lingkup terbatas sehubungan dengan negara setempat.
Saya tidak pernah benar-benar melihat contoh blok anonim yang digunakan seperti ini (tidak melibatkan persyaratan,
try
blok untuk transaksi, dll) untuk mengurangi ruang lingkup dengan cara yang bermakna dalam fungsi yang tidak memunculkan pertanyaan mengapa tidak bisa dibagi lebih jauh menjadi fungsi-fungsi yang lebih sederhana dengan lingkup yang diperkecil jika sebenarnya benar-benar diuntungkan dari sudut pandang SE asli dari blok anonim. Biasanya kode eklektik yang melakukan banyak hal yang terkait atau sangat tidak terkait di mana kita paling tergoda untuk mencapai ini.Sebagai contoh, jika Anda mencoba melakukan ini untuk menggunakan kembali variabel bernama
count
, maka itu menyarankan Anda menghitung dua hal yang berbeda. Jika nama variabel akan sesingkatcount
, maka masuk akal bagi saya untuk mengikatnya dengan konteks fungsi, yang berpotensi hanya menghitung satu jenis hal. Kemudian Anda dapat langsung melihat nama fungsi dan / atau dokumentasi, melihatcount
, dan langsung tahu apa artinya dalam konteks fungsi apa yang dilakukan tanpa menganalisis semua kode. Saya tidak sering menemukan argumen yang baik untuk fungsi menghitung dua hal yang berbeda menggunakan kembali nama variabel yang sama dengan cara yang membuat lingkup / blok anonim begitu menarik dibandingkan dengan alternatif. Itu tidak menyarankan bahwa semua fungsi harus menghitung hanya satu hal. SAYA'manfaat rekayasa untuk fungsi menggunakan kembali nama variabel yang sama untuk menghitung dua hal atau lebih dan menggunakan blok anonim untuk membatasi ruang lingkup setiap hitungan individu. Jika fungsi ini sederhana dan jelas, ini bukan akhir dunia untuk memiliki dua variabel jumlah yang dinamai berbeda dengan yang pertama mungkin memiliki beberapa garis visibilitas ruang lingkup lebih dari yang dibutuhkan secara ideal. Fungsi seperti itu umumnya bukan sumber kesalahan yang kurang memiliki blok anonim seperti itu untuk mengurangi ruang lingkup minimum variabel lokalnya lebih jauh.Bukan Saran untuk Metode Berlebihan
Ini bukan untuk menyarankan Anda secara paksa membuat metode hanya untuk mengurangi ruang lingkup. Itu bisa dibilang sama buruk atau lebih buruknya, dan apa yang saya sarankan tidak harus menyerukan perlunya metode "pembantu" pribadi yang canggung lebih daripada kebutuhan akan lingkup anonim. Itu berpikir terlalu banyak tentang kode seperti sekarang dan bagaimana mengurangi ruang lingkup variabel daripada berpikir tentang bagaimana memecahkan masalah secara konseptual pada tingkat antarmuka dengan cara yang menghasilkan bersih, visibilitas singkat dari fungsi lokal secara alami tanpa blok penyangga yang dalam. dan 6+ level indentasi. Saya setuju dengan Bruno bahwa Anda dapat menghalangi pembacaan kode dengan secara paksa mendorong 3 baris kode ke dalam suatu fungsi, tetapi itu dimulai dengan asumsi bahwa Anda mengembangkan fungsi yang Anda buat berdasarkan implementasi yang ada, daripada mendesain fungsi tanpa terlibat dalam implementasi. Jika Anda melakukannya dengan cara yang terakhir, saya telah menemukan sedikit kebutuhan untuk blok anonim yang tidak melayani tujuan selain mengurangi ruang lingkup variabel dalam metode yang diberikan kecuali Anda mencoba dengan penuh semangat untuk mengurangi ruang lingkup variabel dengan hanya beberapa baris kode berbahaya. di mana pengenalan eksotis dari blok anonim ini bisa dibilang menyumbang overhead intelektual sebanyak yang mereka keluarkan.
Mencoba Mengurangi Lingkup Minimum Lebih Jauh
Jika mengurangi lingkup variabel lokal ke minimum absolut bermanfaat, maka harus ada penerimaan luas kode seperti ini:
... karena hal itu menyebabkan visibilitas minimum negara dengan bahkan tidak membuat variabel untuk merujuk mereka di tempat pertama. Saya tidak ingin tampil sebagai dogmatis, tetapi saya benar-benar berpikir solusi pragmatis adalah menghindari blok anonim jika mungkin seperti halnya menghindari garis kode yang mengerikan di atas, dan jika mereka tampaknya sangat diperlukan dalam konteks produksi dari perspektif kebenaran dan menjaga invarian dalam suatu fungsi, maka saya pasti berpikir bagaimana Anda mengatur kode Anda ke dalam fungsi dan merancang antarmuka Anda perlu diperiksa ulang. Tentu saja jika metode Anda panjang 400 baris dan ruang lingkup variabel lebih dari 300 baris kode, itu bisa menjadi masalah teknik asli, tetapi itu tidak selalu masalah untuk dipecahkan dengan blok anonim.
Jika tidak ada yang lain, memanfaatkan blok anonim di semua tempat itu eksotis, bukan idiomatik, dan kode eksotis membawa risiko dibenci oleh orang lain, jika bukan diri Anda sendiri, bertahun-tahun kemudian.
Kegunaan Praktis Mengurangi Lingkup
Kegunaan utama mengurangi ruang lingkup variabel adalah untuk memungkinkan Anda mendapatkan manajemen negara yang benar dan menjaganya tetap benar dan memungkinkan Anda untuk dengan mudah bernalar tentang apa yang dilakukan porsi basis kode tertentu - untuk dapat mempertahankan invarian konseptual. Jika manajemen keadaan lokal fungsi tunggal sangat kompleks sehingga Anda harus secara paksa mengurangi ruang lingkup dengan blok anonim dalam kode yang tidak dimaksudkan untuk diselesaikan dan bagus, sekali lagi, itu pertanda bagi saya bahwa fungsi itu sendiri perlu diperiksa ulang . Jika Anda mengalami kesulitan dalam penalaran tentang pengelolaan variabel negara dalam lingkup fungsi lokal, bayangkan kesulitan penalaran tentang variabel pribadi yang dapat diakses oleh setiap metode dari seluruh kelas. Kami tidak dapat menggunakan blok anonim untuk mengurangi visibilitasnya. Bagi saya, memulainya dengan penerimaan bahwa variabel akan cenderung memiliki cakupan yang sedikit lebih luas daripada yang seharusnya mereka miliki dalam banyak bahasa, asalkan tidak sampai pada titik di mana Anda akan kesulitan mempertahankan invarian. Itu bukan sesuatu yang harus dipecahkan dengan blok anonim seperti yang saya lihat sebagai menerima dari sudut pandang pragmatis.
sumber
Saya pikir motivasi adalah alasan yang cukup untuk memperkenalkan penghambat, ya.
Seperti dicatat Casey, blok adalah "bau kode" yang menunjukkan Anda mungkin lebih baik mengekstraksi blok ke dalam suatu fungsi. Tapi kamu mungkin tidak.
John Carmark menulis memo tentang kode inline pada 2007. Ringkasan kesimpulannya
Jadi pikirkan , buat pilihan dengan tujuan, dan (opsional) tinggalkan bukti yang menggambarkan motivasi Anda untuk pilihan yang Anda buat.
sumber
Pendapat saya mungkin kontroversial, tapi ya, saya pikir pasti ada situasi di mana saya akan menggunakan gaya seperti ini. Namun, "hanya untuk mengurangi ruang lingkup variabel" bukan argumen yang valid, karena itulah yang sudah Anda lakukan. Dan melakukan sesuatu hanya untuk melakukannya bukan alasan yang sah. Perhatikan juga bahwa penjelasan ini tidak memiliki arti jika tim Anda telah memutuskan apakah sintaksis semacam ini konvensional atau tidak.
Saya terutama seorang programmer C #, tetapi saya telah mendengarnya di Jawa, mengembalikan kata sandi sebagai
char[]
yang memungkinkan Anda untuk mengurangi waktu kata sandi akan tinggal di memori. Dalam contoh ini, itu tidak akan membantu, karena pengumpul sampah diizinkan untuk mengumpulkan array dari saat itu tidak digunakan, jadi tidak masalah jika kata sandi meninggalkan ruang lingkup. Saya tidak berdebat apakah layak untuk menghapus array setelah Anda selesai dengan itu, tetapi dalam kasus ini, jika Anda ingin melakukannya, pelingkupan variabel masuk akal:Ini sangat mirip dengan coba-dengan-sumber daya , karena ini lingkup sumber daya dan melakukan finalisasi setelah Anda selesai. Sekali lagi harap dicatat bahwa saya tidak berdebat untuk menangani kata sandi dengan cara ini, hanya saja jika Anda memutuskan untuk melakukannya, gaya ini masuk akal.
Alasan untuk ini adalah bahwa variabel tidak lagi valid. Anda telah membuatnya, menggunakannya, dan membatalkan statusnya untuk tidak mengandung informasi yang berarti. Tidak masuk akal untuk menggunakan variabel setelah blok ini, jadi masuk akal untuk lingkup itu.
Contoh lain yang bisa saya pikirkan adalah ketika Anda memiliki dua variabel yang memiliki nama dan makna yang serupa, tetapi Anda bekerja dengan satu dan kemudian dengan yang lain, dan Anda ingin memisahkannya. Saya telah menulis kode ini dalam C #:
Anda dapat berpendapat bahwa saya dapat mendeklarasikan
ILGenerator il;
di bagian atas metode, tetapi saya juga tidak ingin menggunakan kembali variabel untuk objek yang berbeda (pendekatan yang agak fungsional). Dalam hal ini, blok memudahkan untuk memisahkan pekerjaan yang dilakukan, baik secara sintaksis maupun visual. Ia juga memberi tahu bahwa setelah blok, saya selesaiil
dan tidak ada yang mengaksesnya.Argumen terhadap contoh ini menggunakan metode. Mungkin ya, tetapi dalam kasus ini, kodenya tidak terlalu panjang, dan memisahkannya ke dalam metode yang berbeda juga perlu untuk meneruskan semua variabel yang digunakan dalam kode.
sumber