Bagaimana variabel memperkenalkan negara?

11

Saya sedang membaca "Standar Pengodean C ++" dan baris ini ada di sana:

Variabel memperkenalkan keadaan, dan Anda harus berurusan dengan keadaan sesedikit mungkin, dengan masa hidup sesingkat mungkin.

Bukankah sesuatu yang bermutasi pada akhirnya memanipulasi keadaan? Apa yang harus Anda hadapi dengan keadaan sesedikit mungkin artinya?

Dalam bahasa yang tidak murni seperti C ++, bukankah manajemen negara benar-benar melakukan apa yang Anda lakukan? Dan apa cara lain untuk berurusan dengan keadaan sesedikit mungkin selain membatasi variabel seumur hidup?

kunj2aaan
sumber

Jawaban:

16

Bukankah sesuatu yang bisa berubah benar-benar memanipulasi keadaan?

Iya.

Dan apa artinya "Anda harus berurusan dengan keadaan kecil"?

Ini berarti bahwa lebih sedikit negara lebih baik daripada lebih banyak negara. Semakin banyak negara cenderung menimbulkan lebih banyak kompleksitas.

Dalam bahasa yang tidak murni seperti C ++, bukankah manajemen negara benar-benar melakukan apa yang Anda lakukan?

Iya.

Apa cara lain untuk "menangani sedikit keadaan" selain membatasi masa pakai variabel?

Minimalkan jumlah variabel. Isolasikan kode yang memanipulasi beberapa keadaan menjadi unit mandiri sehingga bagian kode lain dapat mengabaikannya.

David Schwartz
sumber
9

Bukankah sesuatu yang bisa berubah benar-benar memanipulasi keadaan?

Iya. Dalam C ++, satu-satunya hal yang bisa berubah adalah constvariabel (non- ).

Dan apa artinya "Anda harus berurusan dengan keadaan kecil"?

Semakin sedikit status program, semakin mudah untuk memahami apa yang dilakukannya. Jadi Anda seharusnya tidak memperkenalkan keadaan yang tidak diperlukan, dan Anda tidak harus menyimpannya begitu Anda tidak lagi membutuhkannya.

Dalam bahasa yang tidak murni seperti C ++, bukankah manajemen negara benar-benar melakukan apa yang Anda lakukan?

Dalam bahasa multi-paradigma seperti C ++, sering ada pilihan antara fungsional "murni" atau pendekatan yang digerakkan oleh negara, atau semacam hibrida. Secara historis, dukungan bahasa untuk pemrograman fungsional telah cukup lemah dibandingkan dengan beberapa bahasa, tetapi membaik.

Apa cara lain untuk "menangani sedikit keadaan" selain membatasi masa pakai variabel?

Batasi ruang lingkup dan masa pakai, untuk mengurangi sambungan antar objek; mendukung variabel lokal daripada global, dan pribadi daripada anggota objek publik.

Mike Seymour
sumber
5

status berarti bahwa sesuatu sedang disimpan di suatu tempat sehingga Anda dapat merujuknya nanti.

Membuat variabel, menciptakan ruang bagi Anda untuk menyimpan beberapa data. Data ini adalah status program Anda.

Anda menggunakannya untuk melakukan sesuatu dengan, mengubahnya, menghitung dengan itu, dll.

Ini adalah keadaan , sedangkan hal-hal yang Anda lakukan tidak menyatakan.

Dalam bahasa fungsional, Anda sebagian besar hanya berurusan dengan fungsi dan melewati fungsi seolah-olah mereka objek. Meskipun fungsi-fungsi ini tidak memiliki keadaan, dan melewati fungsi di sekitar, tidak memperkenalkan negara (selain mungkin di dalam fungsi itu sendiri).

Di C ++ Anda bisa membuat objek fungsi , yang merupakan structatau classtipe yang telah operator()()kelebihan beban. Objek fungsi ini dapat memiliki status lokal, meskipun ini tidak harus dibagi di antara kode lain di program Anda. Functors (yaitu objek fungsi) sangat mudah untuk dilewatkan. Ini sedekat Anda bisa meniru paradigma fungsional dalam C ++. (AFAIK)

Memiliki sedikit atau tanpa status berarti Anda dapat dengan mudah mengoptimalkan program Anda untuk eksekusi paralel, karena tidak ada yang dapat dibagikan di antara utas atau CPU, jadi tidak ada yang dapat diciptakan tentang pertengkaran, dan tidak ada yang harus Anda lindungi terhadap ras data, dll.

Tony The Lion
sumber
2

Yang lain telah memberikan jawaban yang baik untuk 3 pertanyaan pertama.

Dan apa cara lain untuk "menangani keadaan sesedikit mungkin" selain membatasi variabel seumur hidup?

Jawaban kunci untuk pertanyaan # 1 adalah ya, apa pun yang bermutasi akhirnya berdampak pada negara. Kuncinya adalah tidak bermutasi. Jenis yang tidak dapat diubah, menggunakan gaya pemrograman fungsional di mana hasil dari satu fungsi diteruskan langsung ke yang lain dan tidak disimpan, melewati pesan atau acara secara langsung daripada menyimpan status, menghitung nilai alih-alih menyimpan dan memperbarui ...

Kalau tidak, Anda akan membatasi dampak negara; baik melalui visibilitas atau seumur hidup.

Telastyn
sumber
1

Dan apa artinya "Anda harus berurusan dengan keadaan kecil"?

Ini berarti bahwa kelas Anda harus sekecil mungkin, secara optimal mewakili satu abstraksi. Jika Anda menempatkan 10 variabel di kelas Anda, kemungkinan besar Anda melakukan sesuatu yang salah, dan harus melihat bagaimana cara memperbaiki kelas Anda.

BЈовић
sumber
1

Untuk memahami cara kerja suatu program, Anda harus memahami perubahan statusnya. Semakin sedikit status yang Anda miliki dan semakin lokal kode yang menggunakannya, semakin mudah hal ini terjadi.

Jika Anda pernah bekerja dengan program yang memiliki banyak variabel global, Anda akan memahami secara implisit.

Mark tebusan
sumber
1

Status hanya disimpan data. Setiap variabel benar-benar semacam keadaan, tetapi kami biasanya menggunakan "negara" untuk merujuk ke data yang persisten di antara operasi. Sebagai contoh sederhana, tidak berguna, Anda mungkin memiliki kelas yang secara internal menyimpan intdan memiliki increment()dan decrement()fungsi anggota. Di sini, nilai internal adalah keadaan karena ia bertahan selama turunan dari kelas ini. Dengan kata lain, nilainya adalah keadaan objek.

Idealnya, keadaan yang didefinisikan oleh sebuah kelas harus sekecil mungkin dengan redundansi minimal. Ini membantu kelas Anda memenuhi prinsip tanggung jawab tunggal , meningkatkan enkapsulasi, dan mengurangi kompleksitas. Keadaan objek harus sepenuhnya dienkapsulasi oleh antarmuka ke objek itu. Ini berarti bahwa hasil dari operasi apa pun pada objek tersebut akan dapat diprediksi mengingat semantik objek tersebut. Anda selanjutnya dapat meningkatkan enkapsulasi dengan meminimalkan jumlah fungsi yang memiliki akses ke negara .

Ini adalah salah satu alasan utama untuk menghindari negara global. Keadaan global dapat memperkenalkan ketergantungan untuk suatu objek tanpa antarmuka mengekspresikannya, membuat keadaan ini tersembunyi jauh dari pengguna objek. Menjalankan operasi pada objek dengan ketergantungan global mungkin memiliki hasil yang bervariasi dan tidak dapat diprediksi.

Joseph Mansfield
sumber
1

Bukankah sesuatu yang bermutasi pada akhirnya memanipulasi keadaan?

Ya, tetapi jika itu di belakang fungsi anggota kelas kecil yang merupakan satu-satunya entitas di seluruh sistem yang dapat memanipulasi negara privatnya, maka negara itu memiliki cakupan yang sangat sempit.

Apa yang harus Anda hadapi dengan keadaan sesedikit mungkin artinya?

Dari sudut pandang variabel: beberapa baris kode harus dapat mengaksesnya sebaik mungkin. Persempit cakupan variabel ke minimum.

Dari garis sudut pandang kode: karena beberapa variabel harus dapat diakses dari garis kode itu mungkin. Mempersempit jumlah variabel yang baris kode bisa mungkin akses (bahkan tidak peduli bahwa banyak apakah itu tidak mengaksesnya, semua yang penting adalah apakah itu bisa ).

Variabel global sangat buruk karena mereka memiliki ruang lingkup maksimum. Bahkan jika mereka diakses dari 2 baris kode dalam basis kode, dari baris POV kode, variabel global selalu dapat diakses. Dari POV variabel, variabel global dengan tautan eksternal dapat diakses ke setiap baris kode di seluruh basis kode (atau setiap baris kode yang menyertakan header). Meskipun hanya diakses oleh 2 baris kode, jika variabel global dapat dilihat hingga 400.000 baris kode, daftar tersangka langsung Anda ketika Anda menemukan bahwa statusnya tidak sah maka akan memiliki 400.000 entri (mungkin dengan cepat dikurangi menjadi 2 entri dengan alat, tetapi bagaimanapun, daftar langsung akan memiliki 400.000 tersangka dan itu bukan titik awal yang menggembirakan).

Kemungkinannya adalah, juga, bahwa bahkan jika variabel global mulai hanya dimodifikasi oleh 2 baris kode di seluruh basis kode, kecenderungan yang disayangkan dari basis kode untuk berevolusi mundur akan cenderung memiliki jumlah yang meningkat secara drastis, hanya karena itu dapat meningkat sebanyak pengembang, panik untuk memenuhi tenggat waktu, melihat variabel global ini dan menyadari bahwa mereka dapat mengambil jalan pintas melalui itu.

Dalam bahasa yang tidak murni seperti C ++, bukankah manajemen negara benar-benar melakukan apa yang Anda lakukan?

Sebagian besar, ya, kecuali Anda menggunakan C ++ dengan cara yang sangat eksotis yang membuat Anda berurusan dengan struktur data yang tidak dapat diubah dan pemrograman fungsional murni - ini juga sering menjadi sumber sebagian besar bug ketika manajemen negara menjadi kompleks, dan kompleksitasnya adalah sering merupakan fungsi dari visibilitas / pemaparan kondisi itu.

Dan apa cara lain untuk berurusan dengan keadaan sesedikit mungkin selain membatasi variabel seumur hidup?

Semua ini berada di ranah pembatasan ruang lingkup variabel, tetapi ada banyak cara untuk melakukan ini:

  • Hindari variabel global mentah seperti wabah. Bahkan beberapa fungsi setter / pengambil global bodoh mempersempit visibilitas variabel itu secara drastis, dan setidaknya memungkinkan beberapa cara untuk mempertahankan invarian (mis: jika variabel global tidak boleh diizinkan menjadi nilai negatif, setter dapat mempertahankan invarian itu). Tentu saja, bahkan desain setter / pengambil di atas apa yang seharusnya menjadi variabel global adalah desain yang sangat buruk, maksud saya adalah bahwa itu masih jauh lebih baik.
  • Buat kelas Anda lebih kecil jika memungkinkan. Kelas dengan ratusan fungsi anggota, 20 variabel anggota, dan 30.000 baris kode yang menerapkannya akan memiliki variabel pribadi yang agak "global", karena semua variabel tersebut akan dapat diakses oleh fungsi anggotanya yang terdiri dari 30k baris kode. Anda mungkin mengatakan "kompleksitas negara" dalam kasus itu, sementara mendiskontokan variabel lokal di setiap fungsi anggota, adalah 30,000*20=600,000. Jika ada 10 variabel global yang dapat diakses di atas itu, maka kompleksitas negara mungkin seperti 30,000*(20+10)=900,000. "Kompleksitas keadaan" yang sehat (jenis metrik pribadi saya yang ditemukan) harus dalam ribuan atau di bawah untuk kelas, bukan puluhan ribu, dan jelas bukan ratusan ribu. Untuk fungsi gratis, ucapkan ratusan atau kurang sebelum kita mulai mengalami sakit kepala serius dalam perawatan.
  • Dalam nada yang sama seperti di atas, jangan mengimplementasikan sesuatu sebagai fungsi anggota atau fungsi teman yang dapat nonmember, nonfriend hanya menggunakan antarmuka publik kelas. Fungsi tersebut tidak dapat mengakses variabel pribadi kelas, dan dengan demikian mengurangi potensi kesalahan dengan mengurangi ruang lingkup variabel pribadi tersebut.
  • Hindari mendeklarasikan variabel jauh sebelum mereka benar-benar dibutuhkan dalam suatu fungsi (yaitu, hindari gaya warisan C yang menyatakan semua variabel di bagian atas fungsi bahkan jika mereka hanya membutuhkan banyak baris di bawah ini). Jika Anda tetap menggunakan gaya ini, setidaknya usahakan untuk fungsi yang lebih pendek.

Beyond Variables: Efek Samping

Banyak pedoman yang saya sebutkan di atas adalah menangani akses langsung ke keadaan mentah (variabel). Namun dalam basis kode yang cukup kompleks, hanya mempersempit ruang lingkup variabel mentah tidak akan cukup untuk dengan mudah alasan tentang kebenaran.

Anda dapat memiliki, katakanlah, struktur data pusat, di balik antarmuka abstrak yang benar-benar PADAT, sepenuhnya mampu mempertahankan invarian dengan sempurna, dan masih berakhir dengan banyak kesedihan karena paparan yang luas dari keadaan sentral ini. Contoh keadaan pusat yang tidak selalu dapat diakses secara global tetapi hanya dapat diakses secara luas adalah grafik adegan pusat dari mesin game atau struktur data lapisan tengah Photoshop.

Dalam kasus seperti itu, gagasan "negara" melampaui variabel mentah, dan hanya untuk struktur data dan hal-hal semacam itu. Ini juga membantu mengurangi ruang lingkup mereka (mengurangi jumlah baris yang dapat memanggil fungsi yang secara tidak langsung bermutasi).

masukkan deskripsi gambar di sini

Perhatikan bagaimana saya dengan sengaja menandai antarmuka sebagai merah di sini, karena dari tingkat arsitektur yang luas dan diperbesar, mengakses antarmuka itu masih bermutasi, meskipun secara tidak langsung. Kelas dapat mempertahankan invarian sebagai hasil dari antarmuka, tetapi itu hanya berjalan sejauh dalam hal kemampuan kita untuk alasan tentang kebenaran.

Dalam hal ini, struktur data pusat berada di belakang antarmuka abstrak yang bahkan mungkin tidak dapat diakses secara global. Ini mungkin hanya disuntikkan dan kemudian secara tidak langsung bermutasi (melalui fungsi anggota) dari sejumlah fungsi di basis kode kompleks Anda.

Dalam kasus seperti itu, bahkan jika struktur data dengan sempurna mempertahankan invariannya sendiri, hal-hal aneh dapat terjadi pada tingkat yang lebih luas (mis: pemutar audio dapat mempertahankan semua jenis invarian seperti bahwa level volume tidak pernah melampaui kisaran 0% hingga 100%, tapi itu tidak melindunginya dari pengguna menekan tombol play dan memiliki klip audio acak selain yang dia baru saja mulai bermain sebagai suatu peristiwa yang dipicu yang menyebabkan daftar putar untuk perombakan dengan cara yang valid tetapi masih perilaku yang tidak diinginkan, glitchy dari perspektif pengguna luas).

Cara untuk melindungi diri Anda sendiri dalam skenario kompleks ini adalah "menyumbat" tempat-tempat dalam basis kode yang dapat memanggil fungsi yang pada akhirnya menyebabkan efek samping eksternal bahkan dari pandangan yang lebih luas tentang sistem yang melampaui keadaan mentah dan di luar antarmuka.

masukkan deskripsi gambar di sini

Aneh seperti ini terlihat, Anda dapat melihat bahwa tidak ada "negara" (ditampilkan dalam warna merah, dan ini tidak berarti "variabel mentah", itu hanya berarti "objek" dan bahkan mungkin di belakang antarmuka abstrak) sedang diakses oleh banyak tempat . Fungsi masing-masing memiliki akses ke negara lokal yang juga dapat diakses oleh pusat pembaruan, dan negara pusat hanya dapat diakses oleh pusat pembaruan (membuatnya tidak lagi pusat tetapi lebih bersifat lokal).

Ini hanya untuk basis kode yang benar-benar kompleks, seperti permainan yang menjangkau 10 juta baris kode, tetapi ini dapat sangat membantu dalam mempertimbangkan kebenaran perangkat lunak Anda, dan menemukan bahwa perubahan Anda menghasilkan hasil yang dapat diprediksi, ketika Anda secara signifikan membatasi / menghambat angka tersebut tempat yang dapat bermutasi menyatakan kritis bahwa seluruh arsitektur berputar untuk berfungsi dengan benar.

Di luar variabel mentah adalah efek samping eksternal, dan efek samping eksternal adalah sumber kesalahan bahkan jika mereka terbatas pada beberapa fungsi anggota. Jika satu muatan fungsi kapal dapat secara langsung memanggil beberapa fungsi anggota tersebut, maka ada muatan fungsi dalam sistem yang secara tidak langsung dapat menyebabkan efek samping eksternal, dan yang meningkatkan kompleksitas. Jika hanya ada satu tempat di basis kode yang memiliki akses ke fungsi anggota tersebut, dan bahwa satu jalur eksekusi tidak dipicu oleh peristiwa sporadis di semua tempat, tetapi dijalankan dengan cara yang sangat terkontrol dan dapat diprediksi, maka itu mengurangi kompleksitas.

Kompleksitas Negara

Bahkan kompleksitas negara adalah faktor yang agak penting untuk dipertimbangkan. Struktur sederhana, dapat diakses secara luas di belakang antarmuka abstrak, tidak begitu sulit untuk dikacaukan.

Struktur data grafik kompleks yang mewakili representasi logis inti dari arsitektur kompleks cukup mudah untuk dikacaukan, dan dengan cara yang bahkan tidak melanggar invarian grafik. Grafik berkali-kali lebih kompleks daripada struktur sederhana, dan itu menjadi lebih penting dalam kasus seperti itu untuk mengurangi kompleksitas yang dirasakan dari basis kode untuk mengurangi jumlah tempat yang memiliki akses ke struktur grafik sedemikian hingga minimum absolut, dan di mana strategi "pusat pembaruan" semacam itu yang membalikkan paradigma tarikan untuk menghindari sporadis, dorongan langsung ke struktur data grafik dari semua tempat dapat benar-benar terbayar.


sumber