Mengapa Negara Global begitu Jahat?

328

Sebelum kita mulai ini, izinkan saya mengatakan saya sangat menyadari konsep Abstraction and Dependency Injection. Saya tidak perlu membuka mata saya di sini.

Yah, kebanyakan dari kita mengatakan, (terlalu) berkali-kali tanpa benar-benar memahami, "Jangan gunakan variabel global", atau "Lajang jahat karena mereka global". Tapi apa benar-benar adalah begitu buruk tentang negara global menyenangkan?

Katakanlah saya memerlukan konfigurasi global untuk aplikasi saya, misalnya jalur folder sistem, atau kredensial basis data aplikasi.

Dalam hal ini, saya tidak melihat solusi yang baik selain menyediakan pengaturan ini dalam semacam ruang global, yang biasanya tersedia untuk seluruh aplikasi.

Aku tahu itu buruk untuk menyalahgunakan itu, tetapi adalah ruang global benar-benar BAHWA jahat? Dan jika ya, alternatif apa yang bagus?

Madara Uchiha
sumber
5
Anda dapat menggunakan kelas config yang menangani akses ke pengaturan tersebut. Saya pikir praktik yang baik untuk memiliki satu dan hanya satu tempat di mana pengaturan konfigurasi dibaca dari file config dan / atau database sementara objek lain mengambilnya dari hal config. itu membuat desain Anda lebih bersih dan dapat diprediksi.
Hajo
25
Di mana bedanya dengan menggunakan global? "Satu-satunya tempat" Anda terdengar sangat mencurigakan seperti Singleton.
Madara Uchiha
130
Satu-satunya kejahatan nyata adalah dogma.
Pieter B
34
Menggunakan keadaan global dengan sesama programmer Anda seperti menggunakan sikat gigi yang sama dengan teman-teman Anda - Anda bisa tetapi Anda tidak pernah tahu kapan seseorang akan memutuskan untuk mendorong gelandangannya.
ziGi
5
Iblis itu jahat. Negara global bukanlah kejahatan atau kebaikan. Ini bisa sangat berguna atau berbahaya tergantung pada bagaimana Anda menggunakannya. Jangan dengarkan orang lain dan pelajari cara memprogram dengan benar.
annoying_squid

Jawaban:

282

Sangat singkat, itu membuat keadaan program tidak dapat diprediksi.

Untuk menguraikan, bayangkan Anda memiliki beberapa objek yang keduanya menggunakan variabel global yang sama. Dengan asumsi Anda tidak menggunakan sumber keacakan mana pun dalam modul mana pun, maka output dari metode tertentu dapat diprediksi (dan karenanya diuji) jika keadaan sistem diketahui sebelum Anda menjalankan metode tersebut.

Namun, jika metode di salah satu objek memicu efek samping yang mengubah nilai negara global bersama, maka Anda tidak lagi tahu apa kondisi awal ketika Anda menjalankan metode di objek lain. Anda sekarang tidak dapat lagi memprediksi output apa yang akan Anda dapatkan ketika Anda menjalankan metode ini, dan karenanya Anda tidak dapat mengujinya.

Pada tingkat akademis, ini mungkin kedengarannya tidak terlalu serius, tetapi kemampuan untuk menyatukan kode tes adalah langkah utama dalam proses pembuktian kebenarannya (atau setidaknya kesesuaian untuk tujuan tertentu).

Di dunia nyata, ini dapat memiliki beberapa konsekuensi yang sangat serius. Misalkan Anda memiliki satu kelas yang mengisi struktur data global, dan kelas lain yang mengkonsumsi data dalam struktur data itu, mengubah keadaannya atau menghancurkannya dalam proses. Jika kelas prosesor mengeksekusi metode sebelum kelas populator dilakukan, hasilnya adalah kelas prosesor mungkin akan memiliki data yang tidak lengkap untuk diproses, dan struktur data yang dikerjakan kelas populator dapat rusak atau hancur. Perilaku program dalam keadaan ini menjadi benar-benar tidak dapat diprediksi, dan mungkin akan menyebabkan kehilangan epik.

Lebih lanjut, keadaan global merusak keterbacaan kode Anda. Jika kode Anda memiliki ketergantungan eksternal yang tidak secara eksplisit dimasukkan ke dalam kode, maka siapa pun yang mendapatkan pekerjaan mempertahankan kode Anda harus mencarinya untuk mencari tahu dari mana asalnya.

Adapun alternatif apa yang ada, tidak mungkin untuk tidak memiliki negara global sama sekali, tetapi dalam praktiknya biasanya mungkin untuk membatasi negara global untuk satu objek yang membungkus semua yang lain, dan yang tidak boleh dirujuk dengan mengandalkan aturan pelingkupan dari bahasa yang Anda gunakan. Jika suatu objek tertentu membutuhkan keadaan tertentu, maka ia harus secara eksplisit memintanya dengan mengirimkannya sebagai argumen kepada konstruktornya atau dengan metode setter. Ini dikenal sebagai Injeksi Ketergantungan.

Tampaknya konyol untuk lulus dalam keadaan yang sudah bisa Anda akses karena aturan pelingkupan dari bahasa apa pun yang Anda gunakan, tetapi keuntungannya sangat besar. Sekarang jika seseorang melihat kode secara terpisah, jelas status apa yang dibutuhkan dan dari mana asalnya. Ini juga memiliki manfaat besar mengenai fleksibilitas modul kode Anda dan karenanya peluang untuk menggunakannya kembali dalam konteks yang berbeda. Jika status diteruskan dan perubahan ke keadaan adalah lokal untuk blok kode, maka Anda dapat lulus dalam keadaan apa pun yang Anda suka (jika itu tipe data yang benar) dan minta kode Anda memprosesnya. Kode yang ditulis dalam gaya ini cenderung memiliki penampilan kumpulan komponen yang terkait longgar yang dapat dengan mudah dipertukarkan. Kode modul seharusnya tidak peduli dari mana asalnya, hanya bagaimana memprosesnya.

Ada banyak alasan lain mengapa melintas negara jauh lebih unggul daripada mengandalkan negara global. Jawaban ini sama sekali tidak komprehensif. Anda mungkin bisa menulis seluruh buku tentang mengapa keadaan global buruk.

GordonM
sumber
61
Pada dasarnya, karena siapa pun dapat mengubah negara, Anda tidak dapat mengandalkannya.
Oded
21
@ Kebenaran Alternatifnya? Injeksi Ketergantungan .
rdlowrey
12
Argumen utama Anda tidak benar-benar berlaku untuk sesuatu yang hanya dapat dibaca secara efektif, seperti objek yang mewakili konfigurasi.
frankc
53
Tidak ada yang menjelaskan mengapa variabel global hanya baca buruk ... yang secara eksplisit ditanyakan oleh OP. Ini adalah jawaban yang baik, saya hanya terkejut bahwa OP menandainya sebagai "diterima" ketika ia secara eksplisit membahas poin yang berbeda.
Konrad Rudolph
20
being able to unit test code is a major step in the process of proving its correctness (or at least fitness for purpose). Bukan itu. "Sekarang sudah dua dekade sejak ditunjukkan bahwa pengujian program dapat secara meyakinkan menunjukkan keberadaan bug, tetapi tidak pernah dapat menunjukkan ketidak hadiran mereka. Setelah mengutip pernyataan yang dipublikasikan dengan seksama ini, insinyur perangkat lunak kembali ke urutan hari itu dan melanjutkan untuk memperbaiki strategi pengujiannya, seperti alkemis dahulu kala, yang terus menyempurnakan pemurnian chrysocosmic-nya. " - Djikstra, 1988. (Itu membuatnya 4,5 dekade sekarang ...)
Mason Wheeler
135

Negara global yang bisa berubah adalah kejahatan karena berbagai alasan:

  • Bug dari keadaan global yang bisa berubah - banyak bug yang rumit disebabkan oleh kemampuan berubah-ubah. Bug yang dapat disebabkan oleh mutasi dari mana saja dalam program ini bahkan lebih sulit, karena seringkali sulit untuk melacak penyebab pastinya.
  • Testabilitas yang buruk - jika Anda memiliki status global yang dapat berubah, Anda harus mengonfigurasinya untuk setiap tes yang Anda tulis. Ini membuat pengujian lebih sulit (dan karena itu orang yang menjadi orang cenderung melakukannya!). misalnya dalam hal kredensial basis data aplikasi, bagaimana jika satu tes perlu mengakses database uji khusus yang berbeda dari yang lainnya?
  • Ketidakfleksibelan - bagaimana jika satu bagian dari kode memerlukan satu nilai di negara global, tetapi bagian lain membutuhkan nilai lain (misalnya nilai sementara selama transaksi)? Anda tiba-tiba memiliki sedikit refactoring buruk di tangan Anda
  • Pengotor fungsi - fungsi "murni" (yaitu yang hasilnya tergantung hanya pada parameter input dan tidak memiliki efek samping) jauh lebih mudah untuk dipikirkan dan disusun untuk membangun program yang lebih besar. Fungsi yang membaca atau memanipulasi keadaan global yang bisa berubah secara inheren tidak murni.
  • Pemahaman kode - perilaku kode yang tergantung pada banyak variabel global yang bisa berubah jauh lebih sulit untuk dipahami - Anda perlu memahami rentang interaksi yang mungkin dengan variabel global sebelum Anda dapat mempertimbangkan perilaku kode tersebut. Dalam beberapa situasi, masalah ini bisa menjadi tidak bisa dipecahkan.
  • Masalah konkurensi - keadaan global yang bisa berubah biasanya memerlukan beberapa bentuk penguncian ketika digunakan dalam situasi bersamaan. Ini sangat sulit untuk diperbaiki (merupakan penyebab bug) dan menambah kompleksitas kode Anda (sulit / mahal untuk dipelihara).
  • Kinerja - banyak utas yang terus-menerus melakukan bashing pada kondisi global yang sama menyebabkan pertikaian cache dan akan memperlambat sistem Anda secara keseluruhan.

Alternatif untuk keadaan global yang bisa berubah:

  • Parameter fungsi - sering diabaikan, tetapi parameterisasi fungsi Anda lebih baik seringkali merupakan cara terbaik untuk menghindari keadaan global. Ini memaksa Anda untuk menyelesaikan pertanyaan konseptual penting: informasi apa yang dibutuhkan fungsi ini untuk melakukan tugasnya? Terkadang masuk akal untuk memiliki struktur data yang disebut "Konteks" yang dapat diturunkan melalui rangkaian fungsi yang merangkum semua informasi yang relevan.
  • Ketergantungan injeksi - sama seperti untuk parameter fungsi, hanya dilakukan sedikit lebih awal (pada konstruksi objek daripada pemanggilan fungsi). Hati-hati jika dependensi Anda adalah objek yang bisa berubah, ini dapat dengan cepat menyebabkan masalah yang sama dengan keadaan global yang bisa berubah .....
  • Negara global yang tidak dapat berubah sebagian besar tidak berbahaya - secara efektif konstan. Tetapi pastikan itu benar-benar konstan, dan bahwa Anda tidak akan tergoda untuk mengubahnya menjadi negara global yang bisa berubah di kemudian hari!
  • Lajang yang tidak dapat berubah - hampir sama dengan negara global yang tidak dapat berubah, kecuali bahwa Anda dapat menunda Instansiasi sampai mereka dibutuhkan. Berguna untuk misalnya struktur data tetap besar yang membutuhkan pra-perhitungan satu kali yang mahal. Lajang yang bisa berubah tentu saja setara dengan negara global yang bisa berubah dan karena itu jahat :-)
  • Pengikatan dinamis - hanya tersedia di beberapa bahasa seperti Common Lisp / Clojure, tetapi ini secara efektif memungkinkan Anda mengikat nilai dalam lingkup terkontrol (biasanya berdasarkan thread-lokal) yang tidak mempengaruhi utas lainnya. Hingga taraf tertentu ini adalah cara "aman" untuk mendapatkan efek yang sama dengan variabel global, karena Anda tahu bahwa hanya utas eksekusi saat ini yang akan terpengaruh. Ini sangat berguna dalam kasus di mana Anda memiliki beberapa utas yang masing-masing menangani transaksi independen, misalnya.
mikera
sumber
11
Saya pikir bahwa melewati objek konteks baik dengan parameter fungsi atau injeksi ketergantungan akan menyebabkan masalah jika konteksnya bisa berubah, masalah yang sama seperti menggunakan keadaan global yang bisa berubah.
Alfredo Osorio
1
Semua barang bagus, dan amin! Tetapi pertanyaannya adalah tentang negara global yang tidak berubah
MarkJ
@ Alfredo - sangat benar. Meskipun tidak seperti yang buruk karena setidaknya lingkup dapat agak dikontrol. Tetapi secara umum, lebih mudah untuk hanya menyelesaikan masalah dengan membuat konteks dan dependensi tidak berubah.
mikera
2
+1 untuk bisa berubah / tidak berubah. Global yang tidak dapat berubah tidak masalah. Bahkan yang malas dimuat tetapi tidak pernah berubah. Tentu saja, jangan memaparkan variabel global, tetapi antarmuka global atau API.
Jess
1
@giorgio Pertanyaannya memperjelas bahwa variabel yang dipermasalahkan mendapatkan nilainya saat startup dan tidak pernah berubah setelahnya selama eksekusi program (folder sistem, kredensial basis data). Yaitu abadi, itu tidak berubah begitu telah diberi nilai. Secara pribadi saya juga menggunakan kata "negara" karena dapat berbeda dari satu eksekusi ke yang lain, atau pada mesin yang berbeda. Mungkin ada kata-kata yang lebih baik.
MarkJ
62
  1. Karena seluruh aplikasi sialan Anda dapat menggunakannya, selalu sangat sulit untuk memfaktorkannya kembali. Jika Anda pernah mengubah sesuatu yang berkaitan dengan global Anda, semua kode Anda perlu diubah. Ini adalah sakit kepala pemeliharaan - jauh lebih dari sekadar mampu grepuntuk nama jenis untuk mengetahui fungsi mana yang menggunakannya.
  2. Mereka buruk karena mereka memperkenalkan dependensi tersembunyi, yang memecah multithreading, yang semakin vital untuk semakin banyak aplikasi.
  3. Keadaan variabel global selalu sama sekali tidak bisa diandalkan, karena semua kode Anda bisa melakukan apa saja untuk itu.
  4. Mereka sangat sulit untuk diuji.
  5. Mereka membuat panggilan API sulit. "Anda harus ingat untuk menelepon SET_MAGIC_VARIABLE () sebelum memanggil API" hanya memohon seseorang yang lupa menyebutnya. Itu membuat menggunakan API rawan kesalahan, menyebabkan bug yang sulit ditemukan. Dengan menggunakannya sebagai parameter biasa, Anda memaksa penelepon untuk memberikan nilai dengan benar.

Cukup berikan referensi ke fungsi yang membutuhkannya. Tidak sesulit itu.

DeadMG
sumber
3
Nah, Anda dapat memiliki kelas konfigurasi global yang merangkum penguncian, dan IS dirancang untuk mengubah keadaan kapan pun memungkinkan. Saya akan memilih pendekatan ini daripada instantiating pembaca konfigurasi dari 1000x tempat dalam kode. Tapi ya, ketidakpastian adalah hal yang paling buruk tentang mereka.
Coder
6
@ Kode: Perhatikan bahwa alternatif yang waras untuk global bukan "konfigurasi pembaca dari 1000x tempat dalam kode", tetapi satu pembaca konfigurasi, yang membuat objek konfigurasi, yang metode dapat menerima sebagai parameter (-> injeksi Ketergantungan).
sleske
2
Nitpicking: Mengapa lebih mudah memahami jenis daripada global? Dan pertanyaannya adalah tentang global hanya baca, jadi poin 2 dan 3 tidak relevan
MarkJ
2
@ MarkJ: Saya berharap bahwa tidak ada yang pernah, katakanlah, membayangi nama.
DeadMG
2
@DeadMG, Re ".. selalu benar-benar tidak bisa diandalkan ..", hal yang sama berlaku untuk injeksi ketergantungan. Hanya karena Anda menjadikannya parameter, tidak berarti bahwa obj dijamin memiliki status tetap. Di suatu tempat di bawah fungsi Anda bisa membuat panggilan ke fungsi lain yang mengubah keadaan variabel yang disuntikkan di bagian atas.
Pacerier
34

Jika Anda mengatakan "keadaan", itu biasanya berarti "keadaan bisa berubah". Dan keadaan global yang bisa berubah benar - benar jahat, karena itu berarti bahwa setiap bagian dari program dapat mempengaruhi bagian lain mana pun (dengan mengubah negara global).

Bayangkan men-debug program yang tidak dikenal: Anda menemukan bahwa fungsi A berperilaku dengan cara tertentu untuk parameter input tertentu, tetapi kadang-kadang berfungsi berbeda untuk parameter yang sama. Anda menemukan bahwa ia menggunakan variabel global x .

Anda mencari tempat yang memodifikasi x , dan menemukan bahwa ada lima tempat yang memodifikasinya. Sekarang semoga berhasil menemukan dalam kasus apa fungsi A melakukan apa ...

sleske
sumber
Jadi negara global yang kekal tidak begitu jahat?
FrustratedWithFormsDesigner
50
Saya akan mengatakan bahwa negara global yang tidak berubah adalah praktik baik yang dikenal sebagai 'konstanta'.
Telastyn
6
Keadaan global yang tidak berubah bukanlah kejahatan, hanya saja buruk :-). Ini masih bermasalah karena kopling yang diperkenalkannya (membuat perubahan, penggunaan kembali, dan pengujian unit lebih sulit), tetapi menciptakan lebih sedikit masalah, jadi dalam kasus-kasus sederhana biasanya dapat diterima.
sleske
2
IFF satu memang menggunakan variabel global maka hanya sepotong kode yang harus memodifikasinya. Selebihnya bebas untuk membacanya. Masalah orang lain yang mengubahnya tidak hilang dengan enkapsulasi dan fungsi akses. Ini bukan untuk apa konstruksi itu.
phkahler
1
@Pacerier: Ya, mengubah antarmuka yang banyak digunakan sulit, tidak peduli apakah itu digunakan sebagai variabel global atau lokal. Namun, itu tidak tergantung pada poin saya, yaitu bahwa interaksi potongan kode yang berbeda sulit untuk dipahami dengan vars global.
sleske
9

Anda semacam menjawab pertanyaan Anda sendiri. Mereka sulit dikelola ketika 'dilecehkan,' tetapi dapat bermanfaat dan [agak] dapat diprediksi bila digunakan dengan benar, oleh seseorang yang tahu bagaimana cara mengatasinya. Pemeliharaan dan perubahan pada / pada global biasanya merupakan mimpi buruk, diperparah dengan meningkatnya ukuran aplikasi.

Pemrogram berpengalaman yang dapat membedakan antara global menjadi satu-satunya pilihan, dan mereka menjadi yang mudah diperbaiki, bisa memiliki sedikit masalah dalam menggunakannya. Tetapi masalah tak berujung yang mungkin timbul dengan penggunaannya mengharuskan saran untuk tidak menggunakannya.

sunting: Untuk mengklarifikasi apa yang saya maksud, global secara alami tidak dapat diprediksi. Seperti halnya hal-hal yang tidak dapat diprediksi, Anda dapat mengambil langkah-langkah untuk mengatasi hal-hal yang tidak dapat diprediksi, tetapi selalu ada batasan untuk apa yang dapat dilakukan. Tambahkan ke ini kerumitan pengembang baru bergabung dengan proyek harus berurusan dengan variabel yang relatif tidak dikenal, rekomendasi terhadap penggunaan global harus dapat dimengerti.

orourkek
sumber
9

Ada banyak masalah dengan Lajang - inilah dua masalah terbesar dalam pikiran saya.

  • Itu membuat pengujian unit bermasalah. Negara global dapat terkontaminasi dari satu tes ke yang berikutnya

  • Ini menegakkan aturan keras "Satu-satunya-satu", yang, meskipun tidak mungkin berubah, tiba-tiba berubah. Sejumlah besar kode utilitas yang menggunakan objek yang dapat diakses secara global kemudian perlu diubah.

Karena itu, sebagian besar sistem memiliki beberapa kebutuhan untuk Objek Global Besar. Ini adalah item yang besar dan mahal (mis. Manajer Koneksi Database), atau menyimpan informasi status meresap (misalnya, mengunci informasi).

Alternatif untuk Singleton adalah membuat Big Global Objects ini dibuat saat startup, dan diteruskan sebagai parameter untuk semua kelas atau metode yang membutuhkan akses ke objek ini.

Masalahnya di sini adalah bahwa Anda berakhir dengan permainan besar "pass-the-parcel". Anda memiliki grafik komponen dan dependensinya, dan beberapa kelas membuat kelas lain, dan masing-masing harus memegang banyak komponen dependensi hanya karena komponen yang ditelurkan (atau komponen komponen yang bertelur) membutuhkannya.

Anda mengalami masalah perawatan baru. Contoh: Tiba-tiba "WidgetFactory" Anda, komponen yang berada jauh di dalam grafik membutuhkan objek pengatur waktu yang ingin Anda tiru. Namun, "WidgetFactory" dibuat oleh "WidgetBuilder" yang merupakan bagian dari "WidgetCreationManager", dan Anda perlu memiliki tiga kelas mengetahui tentang objek pengatur waktu ini meskipun hanya satu yang benar-benar menggunakannya. Anda mendapati diri Anda ingin menyerah dan kembali ke Singletons, dan membuat objek timer ini dapat diakses secara global.

Untungnya, ini adalah masalah yang dipecahkan oleh kerangka kerja Dependency Injection. Anda cukup memberi tahu kerangka kerja kelas apa yang perlu dibuat, dan itu menggunakan refleksi untuk mengetahui grafik dependensi untuk Anda, dan secara otomatis membangun setiap objek saat dibutuhkan.

Jadi, secara ringkas, Lajang itu buruk, dan alternatifnya adalah menggunakan kerangka kerja Ketergantungan.

Saya kebetulan menggunakan Castle Windsor, tetapi Anda dimanja oleh pilihan. Lihat halaman ini dari belakang pada 2008 untuk daftar kerangka kerja yang tersedia.

Andrew Shepherd
sumber
8

Pertama-tama untuk injeksi ketergantungan menjadi "stateful", Anda perlu menggunakan lajang, jadi orang mengatakan ini entah bagaimana alternatif yang salah. Orang-orang menggunakan objek konteks global setiap saat ... Bahkan keadaan sesi misalnya pada dasarnya adalah variabel global. Melewati segala sesuatu di sekitar apakah dengan injeksi ketergantungan atau tidak tidak selalu merupakan solusi terbaik. Saya bekerja pada aplikasi yang sangat besar saat ini yang menggunakan banyak objek konteks global (lajang yang disuntikkan melalui wadah IoC) dan tidak pernah ada masalah untuk debug. Terutama dengan arsitektur yang digerakkan oleh peristiwa, dapat dipilih untuk menggunakan objek konteks global vs. membagikan apa pun yang berubah. Tergantung siapa yang kamu tanya.

Apa pun dapat disalahgunakan dan juga tergantung pada jenis aplikasi. Menggunakan variabel statis misalnya di aplikasi web sama sekali berbeda dari aplikasi desktop. Jika Anda dapat menghindari variabel global, maka lakukanlah, tetapi terkadang mereka memiliki kegunaannya. Paling tidak pastikan data global Anda berada dalam objek kontekstual yang jelas. Sejauh debugging, tidak ada tumpukan panggilan dan beberapa breakpoints tidak dapat menyelesaikan.

Saya ingin menekankan bahwa membabi buta menggunakan variabel global adalah ide yang buruk. Fungsi harus dapat digunakan kembali dan tidak boleh peduli dari mana data berasal - merujuk ke variabel global memasangkan fungsi dengan input data tertentu. Inilah sebabnya mengapa itu harus diteruskan dan mengapa injeksi ketergantungan dapat membantu, meskipun Anda masih berurusan dengan toko konteks terpusat (melalui lajang).

Btw ... Beberapa orang berpikir injeksi ketergantungan itu buruk, termasuk pembuat Linq, tetapi itu tidak akan menghentikan orang untuk menggunakannya, termasuk saya. Pada akhirnya pengalaman akan menjadi guru terbaik Anda. Ada waktu untuk mengikuti aturan dan ada waktu untuk melanggarnya.

KingOfHypocrites
sumber
4

Sejak beberapa jawaban lain di sini membuat perbedaan antara bisa berubah dan berubah negara global, saya ingin berpendapat bahwa bahkan berubah variabel global / pengaturan sering jengkel .

Pertimbangkan pertanyaannya:

... Katakanlah saya memerlukan konfigurasi global untuk aplikasi saya, misalnya jalur folder sistem, atau kredensial basis data aplikasi. ...

Benar, untuk program kecil, ini mungkin bukan masalah, tetapi segera setelah Anda melakukan ini dengan komponen dalam sedikit lebih besar, menulis tes otomatis tiba-tiba menjadi lebih sulit karena semua tes (~ berjalan dalam proses yang sama) harus bekerja dengan nilai konfigurasi global yang sama.

Jika semua data konfigurasi dilewatkan secara eksplisit, komponen menjadi lebih mudah untuk diuji dan Anda tidak perlu khawatir bagaimana mem-bootstrap nilai konfigurasi global untuk beberapa tes, mungkin bahkan secara paralel.

Martin Ba
sumber
3

Nah, untuk satu, Anda bisa lari ke masalah yang sama persis dengan yang Anda bisa dengan lajang. Apa yang hari ini tampak seperti "hal global yang hanya perlu saya" akan tiba-tiba berubah menjadi sesuatu yang Anda perlukan lebih banyak.

Misalnya, hari ini Anda membuat sistem konfigurasi global ini karena Anda menginginkan satu konfigurasi global untuk seluruh sistem. Beberapa tahun ke depan, Anda port ke sistem lain dan seseorang berkata, "hei, Anda tahu, ini mungkin bekerja lebih baik jika ada satu konfigurasi global umum dan satu konfigurasi platform spesifik." Tiba-tiba Anda memiliki semua pekerjaan ini untuk membuat struktur global Anda tidak global, sehingga Anda dapat memiliki banyak struktur.

(Ini bukan contoh acak ... ini terjadi dengan sistem konfigurasi kami dalam proyek saya saat ini.)

Menimbang bahwa biaya untuk membuat sesuatu yang non-global biasanya sepele, itu konyol untuk melakukannya. Anda hanya menciptakan masalah di masa depan.

Steven Burnap
sumber
Contoh Anda bukan yang dimaksud OP. Anda jelas-jelas berbicara tentang instantiasi konfigurasi abstrak (hanya struktur data manajer konfigurasi, tanpa kunci preferensi spesifik aplikasi) beberapa kali karena Anda ingin membagi semua kunci dalam instance yang berjalan dari program Anda di beberapa instance kelas manajer konfigurasi Anda. (Katakanlah, setiap instance dapat menangani satu file konfigurasi, misalnya).
Jo So
3

Masalah lainnya adalah mereka membuat aplikasi sulit untuk diukur karena mereka tidak cukup "global". Cakupan variabel global adalah prosesnya.

Jika Anda ingin meningkatkan aplikasi Anda dengan menggunakan beberapa proses atau dengan menjalankan pada beberapa server Anda tidak bisa. Setidaknya tidak sampai Anda memperhitungkan semua global dan menggantinya dengan mekanisme lain.

James Anderson
sumber
3

Mengapa Negara Global begitu Jahat?

Yg mungkin berubahKeadaan global yang bisa adalah kejahatan karena sangat sulit bagi otak kita untuk memperhitungkan lebih dari beberapa parameter sekaligus dan mencari tahu bagaimana mereka menggabungkan keduanya dari perspektif waktu dan perspektif nilai untuk memengaruhi sesuatu.

Oleh karena itu, kami sangat buruk dalam debugging atau menguji suatu objek yang perilakunya memiliki lebih dari beberapa alasan eksternal untuk diubah selama pelaksanaan suatu program. Apalagi ketika kita harus bernalar tentang lusinan benda yang disatukan.

guillaume31
sumber
3

Bahasa yang dirancang untuk desain sistem yang aman & tangguh sering kali menyingkirkan kondisi global yang bisa berubah sama sekali. (Bisa dibilang ini berarti tidak ada global, karena objek yang tidak dapat diubah dalam arti tidak benar-benar stateful karena mereka tidak pernah memiliki transisi negara.)

Joe-E adalah salah satu contoh, dan David Wagner menjelaskan keputusan sebagai berikut:

Analisis tentang siapa yang memiliki akses ke suatu objek dan prinsip privilege paling rendah ditumbangkan ketika kemampuan disimpan dalam variabel global dan dengan demikian berpotensi dibaca oleh bagian mana pun dari program. Setelah suatu objek tersedia secara global, maka tidak mungkin lagi membatasi ruang lingkup analisis: akses ke objek adalah hak istimewa yang tidak dapat ditahan dari kode apa pun dalam program. Joe-E menghindari masalah ini dengan memverifikasi bahwa ruang lingkup global tidak mengandung kemampuan, hanya data yang tidak dapat diubah.

Jadi salah satu cara untuk memikirkannya adalah

  1. Pemrograman adalah masalah penalaran terdistribusi. Programmer pada proyek-proyek besar perlu membagi program menjadi beberapa bagian yang dapat dipertimbangkan oleh individu.
  2. Semakin kecil cakupannya, semakin mudah untuk dipikirkan. Ini benar baik individu dan alat analisis statis yang mencoba membuktikan sifat-sifat suatu sistem, dan tes yang perlu menguji sifat-sifat suatu sistem.
  3. Sumber otoritas signifikan yang tersedia secara global membuat properti sistem sulit untuk dipikirkan.

Oleh karena itu, keadaan yang dapat berubah secara global membuatnya menjadi lebih sulit

  1. desain sistem yang kuat,
  2. lebih sulit untuk membuktikan sifat suatu sistem, dan
  3. lebih sulit untuk memastikan bahwa pengujian Anda menguji dalam lingkup yang mirip dengan lingkungan produksi Anda.

Keadaan global yang bisa berubah mirip dengan neraka DLL . Seiring waktu, bagian-bagian berbeda dari sistem besar akan membutuhkan perilaku yang agak berbeda dari bagian-bagian yang sama dari keadaan yang bisa berubah. Memecahkan neraka DLL dan ketidakkonsistenan keadaan yang dapat dibagikan bersama memerlukan koordinasi skala besar antara tim yang berbeda. Masalah-masalah ini tidak akan terjadi seandainya negara global memiliki cakupan yang tepat untuk memulai.

Mike Samuel
sumber
2

Global tidak seburuk itu. Seperti yang dinyatakan dalam beberapa jawaban lain, masalah sebenarnya dengan mereka adalah bahwa apa, hari ini, jalur folder global Anda , besok, bisa menjadi salah satu dari beberapa, atau bahkan ratusan. Jika Anda menulis program cepat, sekali pakai, gunakan global jika lebih mudah. Namun, secara umum, memungkinkan untuk melipatgandakan bahkan ketika Anda hanya berpikir bahwa Anda membutuhkannya adalah cara untuk melakukannya. Tidak menyenangkan harus merestrukturisasi program kompleks besar yang tiba-tiba perlu berbicara dengan dua database.

Tetapi mereka tidak merusak keandalan. Setiap data yang dirujuk dari banyak tempat dalam program Anda dapat menyebabkan masalah jika perubahan tiba-tiba. Enumerator tersedak ketika koleksi yang mereka hitung diubah pada pertengahan enumerasi. Peristiwa antrian acara dapat memainkan trik satu sama lain. Utas selalu dapat mendatangkan kekacauan. Apa pun yang bukan variabel lokal atau bidang yang tidak dapat diubah adalah masalah. Global adalah masalah semacam ini, tetapi Anda tidak akan memperbaikinya dengan menjadikannya non-global.

Jika Anda akan menulis ke file dan jalur folder berubah, perubahan dan penulisan harus disinkronkan. (Sebagai salah satu dari seribu hal yang bisa salah, katakan Anda ambil jalurnya, lalu direktori itu dihapus, lalu jalur folder diubah ke direktori yang baik, lalu Anda coba dan tulis ke direktori yang dihapus.) Masalahnya ada apakah jalur folder bersifat global atau merupakan salah satu dari seribu program yang saat ini digunakan.

Ada masalah nyata dengan bidang yang dapat diakses oleh berbagai peristiwa pada antrian, tingkat rekursi yang berbeda, atau utas berbeda. Untuk membuatnya sederhana (dan sederhana): variabel lokal baik dan bidang buruk. Tetapi para mantan global masih akan menjadi ladang, jadi masalah ini (betapapun pentingnya) tidak berlaku untuk status Baik atau Jahat bidang-bidang Global.

Tambahan: Masalah Multithreading:

(Perhatikan bahwa Anda dapat memiliki masalah serupa dengan antrian acara atau panggilan rekursif, tetapi multithreading sejauh ini adalah yang terburuk.) Pertimbangkan kode berikut:

if (filePath != null)  text = filePath.getName();

Jika filePathadalah variabel lokal atau semacam konstan, program Anda tidak akan gagal ketika berjalan karena filePathnol. Pemeriksaan selalu berhasil. Tidak ada utas lain yang dapat mengubah nilainya. Kalau tidak , tidak ada jaminan. Ketika saya mulai menulis program multithreaded di Java, saya mendapat NullPointerExceptions pada baris seperti ini sepanjang waktu. Apa sajautas lainnya dapat mengubah nilai kapan saja, dan sering kali demikian. Seperti yang ditunjukkan beberapa jawaban lainnya, ini menciptakan masalah serius untuk pengujian. Pernyataan di atas dapat bekerja satu miliar kali, mendapatkannya melalui pengujian yang luas dan komprehensif, kemudian meledak sekali dalam produksi. Pengguna tidak akan dapat mereproduksi masalah, dan itu tidak akan terjadi lagi sampai mereka meyakinkan diri sendiri bahwa mereka melihat sesuatu dan melupakannya.

Global pasti memiliki masalah ini, dan jika Anda dapat menghilangkannya sepenuhnya atau menggantinya dengan konstanta atau variabel lokal, itu adalah hal yang sangat bagus. Jika Anda memiliki kode stateless yang berjalan di server web, Anda mungkin bisa. Biasanya, semua masalah multithreading Anda dapat diambil oleh database.

Tetapi jika program Anda harus mengingat hal-hal dari satu tindakan pengguna ke tindakan berikutnya, Anda akan memiliki bidang yang dapat diakses oleh utas yang berjalan. Mengubah global ke bidang non-global tidak akan membantu keandalan.

RalphChapin
sumber
1
Bisakah Anda mengklarifikasi apa yang Anda maksudkan dengan ini ?: "Apa pun yang bukan variabel lokal atau bidang yang tidak dapat diubah adalah masalah. Global adalah masalah seperti ini, tetapi Anda tidak akan memperbaikinya dengan menjadikannya non-global."
Andres F.
1
@AndresF .: Saya memperpanjang jawaban saya. Saya pikir saya mengambil pendekatan desktop di mana kebanyakan orang di halaman ini lebih banyak server-kode-dengan-database. "Global" dapat memiliki arti berbeda dalam kasus ini.
RalphChapin
2

Status tidak dapat dihindari dalam aplikasi nyata apa pun. Anda dapat membungkusnya sesuka Anda, tetapi spreadsheet harus berisi data dalam sel. Anda bisa membuat objek sel dengan hanya berfungsi sebagai antarmuka, tetapi itu tidak membatasi berapa banyak tempat yang bisa memanggil metode pada sel dan mengubah data. Anda membangun seluruh hierarki objek untuk mencoba menyembunyikan antarmuka sehingga bagian lain dari kode tidak bisaubah data secara default. Itu tidak mencegah referensi ke objek yang mengandung untuk dibagikan secara sewenang-wenang. Semua itu juga tidak menghilangkan masalah konkurensi dengan sendirinya. Itu memang membuat lebih sulit untuk memperbanyak akses ke data, tetapi sebenarnya tidak menghilangkan masalah yang dirasakan dengan global. Jika seseorang ingin memodifikasi bagian dari negara, mereka akan melakukannya dalam kondisi global atau melalui API yang kompleks (yang kemudian hanya akan mencegah, bukan mencegah).

Alasan sebenarnya untuk tidak menggunakan penyimpanan global adalah untuk menghindari tabrakan nama. Jika Anda memuat beberapa modul yang menyatakan nama global yang sama, Anda memiliki perilaku yang tidak terdefinisi (sangat sulit untuk di-debug karena tes unit akan berlalu) atau kesalahan linker (saya pikir C - Apakah linker Anda memperingatkan atau gagal akan hal ini?).

Jika Anda ingin menggunakan kembali kode, Anda harus dapat mengambil modul dari tempat lain dan tidak sengaja menginjak global Anda karena mereka menggunakan satu dengan nama yang sama. Atau jika Anda beruntung dan mendapatkan kesalahan, Anda tidak ingin harus mengubah semua referensi dalam satu bagian kode untuk mencegah tabrakan.

phkahler
sumber
1

Ketika mudah untuk melihat dan mengakses semua negara global, para programmer akhirnya akan melakukannya. Apa yang Anda dapatkan tidak terucapkan dan sulit untuk melacak dependensi (int blahblah berarti array foo valid dalam apa pun). Pada dasarnya itu membuat hampir tidak mungkin untuk mempertahankan invarian program karena semuanya dapat diputar-balikkan secara independen. someInt memiliki hubungan antara OtherInt, itu sulit untuk dikelola dan lebih sulit untuk dibuktikan jika Anda dapat langsung berubah kapan saja.

Yang mengatakan, itu bisa dilakukan (jalan kembali ketika itu adalah satu-satunya cara di beberapa sistem), tetapi keterampilan itu hilang. Mereka berputar sebagian besar di sekitar pengkodean dan penamaan konvensi-bidang telah pindah karena alasan yang baik. Kompiler dan linker Anda melakukan pekerjaan yang lebih baik untuk memeriksa invarian dalam data kelas / modul yang dilindungi / pribadi daripada mengandalkan manusia untuk mengikuti rencana induk dan membaca sumber.

segera
sumber
1
"Tapi keterampilan itu hilang" ... belum sepenuhnya. Saya baru-baru ini bekerja di sebuah rumah peranti lunak yang bersumpah dengan "Clarion", alat pembuat kode yang memiliki bahasa seperti dasar sendiri yang tidak memiliki fitur seperti melewati argumen ke sub-rutin ... Pengembang yang duduk tidak senang dengan saran tentang "mengubah" atau "memodernisasi", akhirnya muak dengan komentar saya dan menggambarkan saya sebagai kurang dan tidak kompeten. Saya harus pergi ...
Louis Somers
1

Saya tidak akan memberi tahu apakah variabel global baik atau buruk, tetapi apa yang akan saya tambahkan ke diskusi adalah untuk memberi tahu fakta bahwa jika Anda tidak menggunakan keadaan global, maka Anda mungkin menghabiskan banyak memori, terutama ketika Anda menggunakan classess untuk menyimpan dependensi mereka di bidang.

Bagi negara global, tidak ada masalah seperti itu, semuanya bersifat global.

Misalnya: bayangkan skenario berikut: Anda memiliki kisi 10x10 yang terbuat dari classess "Board" dan "Tile".

Jika Anda ingin melakukannya dengan cara OOP Anda mungkin akan melewati objek "Papan" ke setiap "Ubin". Katakanlah sekarang bahwa "Tile" memiliki 2 "byte" jenis bidang menyimpan koordinatnya. Total memori yang dibutuhkan oleh mesin 32bit untuk satu ubin adalah (1 + 1 + 4 = 6) byte: 1 untuk x coord, 1 untuk y coord dan 4 untuk sebuah pointer ke papan tulis. Ini memberikan total 600 byte untuk pemasangan ubin 10x10

Sekarang untuk kasus di mana Dewan berada dalam lingkup global, satu objek yang dapat diakses dari setiap ubin Anda hanya perlu mendapatkan 2 byte memori per setiap ubin, itulah byte koordinat x dan y. Ini akan memberikan hanya 200 byte.

Jadi dalam hal ini Anda mendapatkan 1/3 dari penggunaan memori jika Anda hanya menggunakan status global.

Ini, selain hal-hal lain, saya kira adalah alasan mengapa ruang lingkup global masih tetap dalam bahasa (relatif) tingkat rendah seperti C ++

Lukas1985
sumber
1
Itu sangat tergantung pada bahasa. Dalam bahasa di mana program berjalan terus-menerus, mungkin benar bahwa Anda dapat menghemat memori dengan menggunakan ruang global dan lajang alih-alih membuat instance banyak kelas, tetapi bahkan argumen itu goyah. Ini semua tentang prioritas. Dalam bahasa seperti PHP (yang dijalankan sekali per permintaan, dan objek tidak bertahan), bahkan argumen itu diperdebatkan.
Madara Uchiha
1
@MadaraUchiha Tidak, argumen ini tidak goyah dengan cara apa pun. Itu adalah fakta objektif, kecuali beberapa VM atau bahasa yang dikompilasi lainnya melakukan optimasi kode hardcore dengan masalah seperti ini. Kalau tidak, masih relevan. Bahkan dengan program sisi server yang digunakan untuk menjadi "satu tembakan" memori dicadangkan melalui waktu eksekusi. Dengan server beban tinggi ini mungkin menjadi titik kritis.
luke1985
0

Ada beberapa faktor yang perlu dipertimbangkan dengan negara global:

  1. Ruang memori programer
  2. Global abadi / tulis sekali global.
  3. Global yang bisa berubah
  4. Ketergantungan pada global.

Semakin banyak global yang Anda miliki, semakin besar peluang untuk memperkenalkan duplikat, dan dengan demikian memecah hal-hal ketika duplikat keluar dari sinkronisasi. Menjaga semua global dalam ingatan manusia Anda yang goyah menjadi penting dan menyakitkan.

Immutables / write sekali umumnya OK, tetapi hati-hati untuk kesalahan urutan inisialisasi.

Global yang bisa berubah sering keliru sebagai global yang tidak dapat berubah ...

Sebuah fungsi yang menggunakan global secara efektif memiliki parameter "tersembunyi" ekstra, membuat refactoring lebih sulit.

Negara global bukanlah kejahatan, tetapi ia datang dengan biaya yang pasti - gunakan saat manfaatnya melebihi biaya.

jmoreno
sumber