Jika Anda menggunakan kata kunci 'statis' tanpa kata kunci 'final', ini harus menjadi sinyal untuk mempertimbangkan dengan cermat desain Anda. Bahkan kehadiran 'final' bukanlah jalan bebas, karena objek final statis yang dapat berubah bisa sama berbahayanya.
Saya akan memperkirakan sekitar 85% dari waktu saya melihat 'statis' tanpa 'final', itu SALAH. Seringkali, saya akan menemukan solusi aneh untuk menutupi atau menyembunyikan masalah ini.
Tolong jangan membuat mutables statis. Terutama Koleksi. Secara umum, Koleksi harus diinisialisasi ketika objek yang berisi diinisialisasi dan harus dirancang sehingga mereka diatur ulang atau dilupakan ketika objek yang mengandung mereka dilupakan.
Menggunakan statika dapat membuat bug yang sangat halus yang akan menyebabkan hari-hari kesakitan para insinyur. Saya tahu, karena saya telah membuat dan memburu bug ini.
Jika Anda ingin lebih detail, baca terus ...
Kenapa Tidak Menggunakan Statika?
Ada banyak masalah dengan statika, termasuk penulisan dan pelaksanaan tes, serta bug halus yang tidak segera terlihat.
Kode yang bergantung pada objek statis tidak dapat dengan mudah diuji unit, dan statika tidak dapat dengan mudah diejek (biasanya).
Jika Anda menggunakan statika, tidak mungkin untuk menukar implementasi kelas keluar untuk menguji komponen tingkat yang lebih tinggi. Misalnya, bayangkan CustomerDAO statis yang mengembalikan objek Pelanggan yang dimuatnya dari database. Sekarang saya memiliki CustomerFilter kelas, yang perlu mengakses beberapa objek Pelanggan. Jika CustomerDAO statis, saya tidak bisa menulis tes untuk CustomerFilter tanpa terlebih dahulu menginisialisasi database saya dan mengisi informasi yang berguna.
Dan populasi basis data dan inisialisasi membutuhkan waktu lama. Dan dalam pengalaman saya, kerangka inisialisasi DB Anda akan berubah seiring waktu, yang berarti data akan berubah, dan tes mungkin rusak. IE, bayangkan Pelanggan 1 dulu VIP, tetapi kerangka inisialisasi DB berubah, dan sekarang Pelanggan 1 tidak lagi VIP, tetapi tes Anda sulit untuk memuat Pelanggan 1 ...
Pendekatan yang lebih baik adalah dengan instantiate CustomerDAO, dan meneruskannya ke CustomerFilter ketika dibangun. (Pendekatan yang lebih baik lagi adalah dengan menggunakan Spring atau kerangka kerja Inversion of Control lainnya.
Setelah Anda melakukan ini, Anda dapat dengan cepat mengejek atau mematikan DAO alternatif di CustomerFilterTest Anda, yang memungkinkan Anda untuk memiliki kontrol lebih besar atas tes,
Tanpa DAO statis, pengujian akan lebih cepat (tanpa inisialisasi db) dan lebih dapat diandalkan (karena tidak akan gagal ketika kode inisialisasi db berubah). Misalnya, dalam hal ini memastikan Pelanggan 1 adalah dan akan selalu menjadi VIP, sejauh menyangkut pengujian.
Melaksanakan Tes
Statika menyebabkan masalah nyata ketika menjalankan suite unit test bersama (misalnya, dengan server Integrasi Berkelanjutan Anda). Bayangkan peta statis objek Socket jaringan yang tetap terbuka dari satu pengujian ke pengujian lainnya. Tes pertama mungkin membuka Socket pada port 8080, tetapi Anda lupa untuk menghapus Peta saat tes dihancurkan. Sekarang ketika tes kedua diluncurkan, kemungkinan akan macet ketika mencoba membuat Socket baru untuk port 8080, karena port tersebut masih ditempati. Bayangkan juga bahwa referensi Socket di Koleksi statis Anda tidak dihapus, dan (dengan pengecualian WeakHashMap) tidak pernah memenuhi syarat untuk menjadi sampah yang dikumpulkan, menyebabkan kebocoran memori.
Ini adalah contoh yang terlalu umum, tetapi dalam sistem besar, masalah ini terjadi SELAMA WAKTU. Orang-orang tidak memikirkan pengujian unit yang memulai dan menghentikan perangkat lunak mereka berulang kali dalam JVM yang sama, tetapi ini adalah tes yang bagus untuk desain perangkat lunak Anda, dan jika Anda memiliki aspirasi terhadap ketersediaan tinggi, itu adalah sesuatu yang perlu Anda waspadai.
Masalah-masalah ini sering muncul dengan objek kerangka kerja, misalnya, akses DB Anda, caching, pesan, dan lapisan logging. Jika Anda menggunakan Java EE atau beberapa kerangka kerja terbaik, mereka mungkin mengelola banyak hal ini untuk Anda, tetapi jika seperti saya Anda berurusan dengan sistem warisan, Anda mungkin memiliki banyak kerangka kerja khusus untuk mengakses lapisan ini.
Jika konfigurasi sistem yang berlaku untuk komponen kerangka ini berubah di antara pengujian unit, dan kerangka kerja pengujian unit tidak meruntuhkan dan membangun kembali komponen, perubahan ini tidak dapat berlaku, dan ketika tes bergantung pada perubahan itu, mereka akan gagal .
Bahkan komponen non-kerangka kerja tunduk pada masalah ini. Bayangkan sebuah peta statis yang disebut OpenOrders. Anda menulis satu tes yang menciptakan beberapa pesanan terbuka, dan memeriksa untuk memastikan semuanya dalam kondisi yang benar, kemudian tes berakhir. Pengembang lain menulis tes kedua yang menempatkan pesanan yang dibutuhkan ke dalam peta OpenOrders, lalu menegaskan jumlah pesanan yang akurat. Jalankan satu per satu, kedua tes ini akan lulus, tetapi ketika dijalankan bersama dalam sebuah suite, keduanya akan gagal.
Lebih buruk lagi, kegagalan mungkin didasarkan pada urutan di mana tes dijalankan.
Dalam hal ini, dengan menghindari statika, Anda menghindari risiko data yang bertahan di seluruh contoh uji, memastikan keandalan tes yang lebih baik.
Bug Halus
Jika Anda bekerja di lingkungan dengan ketersediaan tinggi, atau di mana pun thread dapat dimulai dan dihentikan, masalah yang sama yang disebutkan di atas dengan unit test suites dapat berlaku ketika kode Anda juga berjalan pada produksi.
Saat berurusan dengan utas, daripada menggunakan objek statis untuk menyimpan data, lebih baik menggunakan objek yang diinisialisasi selama fase startup utas. Dengan cara ini, setiap kali utas dimulai, instance objek baru (dengan konfigurasi yang berpotensi baru) dibuat, dan Anda menghindari data dari satu instance thread yang berdarah hingga instance berikutnya.
Ketika sebuah utas mati, objek statis tidak dapat direset atau sampah dikumpulkan. Bayangkan Anda memiliki utas yang disebut "EmailCustomers", dan ketika mulai itu mengisi kumpulan String statis dengan daftar alamat email, kemudian mulai mengirim email masing-masing alamat. Katakanlah utasnya terputus atau dibatalkan, jadi kerangka kerja ketersediaan tinggi Anda memulai ulang utas. Kemudian ketika utas dimulai, ia memuat kembali daftar pelanggan. Tetapi karena koleksinya statis, ia mungkin mempertahankan daftar alamat email dari koleksi sebelumnya. Sekarang beberapa pelanggan mungkin mendapatkan email rangkap.
An Aside: Final Statis
Penggunaan "final statis" secara efektif setara dengan Java dari definisi C #, meskipun ada perbedaan implementasi teknis. AC / C ++ #define diganti dari kode oleh pra-prosesor, sebelum dikompilasi. Java "static final" akan berakhir dengan memori yang tersimpan di stack. Dengan cara itu, ini lebih mirip dengan variabel "static const" di C ++ daripada ke #define.
Ringkasan
Saya harap ini membantu menjelaskan beberapa alasan dasar mengapa statika bermasalah. Jika Anda menggunakan kerangka kerja Java modern seperti Java EE atau Spring, dll, Anda mungkin tidak menemukan banyak dari situasi ini, tetapi jika Anda bekerja dengan tubuh besar kode warisan, mereka bisa menjadi jauh lebih sering.