なぜグローバル国家はそれほど悪なのか?


328

これを始める前に、抽象化と依存性注入の概念をよく知っていると言っておきましょう。ここで目を開ける必要はありません。

まあ、私たちのほとんどは、「グローバル変数を使用しないでください」、または「シングルトンはグローバルであるため悪です」と本当に理解せずに何度も言います。しかし、不吉なグローバルな状態で本当に悪いの何ですか?

たとえば、システムフォルダパスやアプリケーション全体のデータベース資格情報など、アプリケーションのグローバル構成が必要だとしましょう。

その場合、これらの設定を何らかのグローバルスペースで提供する以外に良い解決策はありません。これは、アプリケーション全体で一般的に利用できます。

私はために悪いことを知っている虐待それを、しかし、世界的なスペースが本当にあるTHAT悪?もしそうなら、どんな良い選択肢がありますか?


5
これらの設定へのアクセスを処理する構成クラスを使用できます。構成ファイルやデータベースから構成設定を読み取る場所を1つだけにして、他のオブジェクトがその構成からそれらを取得することをお勧めします。設計がよりクリーンで予測可能になります。
羽生

25
グローバルを使用することとの違いはどこですか?あなたの「唯一無二の場所」は、疑い深くシングルトンのように聞こえます。
マダラ

130
唯一の本当の悪は教義です。
ピーターB

34
仲間のプログラマーでグローバルステートを使用することは、友達と同じ歯ブラシを使用するようなものです。
-ziGi

5
悪魔は悪です。グローバルな状態は悪でも善でもありません。使用方法に応じて、非常に有用または有害になる可能性があります。他の人の言うことを聞かないで、適切にプログラムする方法を学んでください。
annoying_squid

回答:


282

非常に簡単に言えば、プログラムの状態を予測不能にします。

詳しく説明すると、同じグローバル変数を使用するオブジェクトがいくつかあると想像してください。いずれかのモジュール内でランダム性のソースを使用していないと仮定すると、メソッドを実行する前にシステムの状態がわかっている場合、特定のメソッドの出力を予測(したがってテスト)できます。

ただし、いずれかのオブジェクトのメソッドが、共有グローバル状態の値を変更する副作用を引き起こす場合、もう一方のオブジェクトのメソッドを実行したときの開始状態はわかりません。これで、メソッドを実行したときにどの出力が得られるかを予測できなくなり、テストできなくなりました。

アカデミックレベルでは、これはそれほど深刻なことではないかもしれませんが、コードを単体テストできることは、その正確性(または少なくとも目的への適合性)を証明するプロセスの主要なステップです。

現実の世界では、これは非常に深刻な結果をもたらす可能性があります。グローバルデータ構造にデータを取り込む1つのクラスと、そのデータ構造内のデータを消費し、状態を変更するか、プロセスでデータを破棄する別のクラスがあるとします。ポピュレータークラスが完了する前にプロセッサークラスがメソッドを実行すると、プロセッサークラスが処理するデータが不完全になる可能性があり、ポピュレータークラスが処理していたデータ構造が破損または破壊される可能性があります。これらの状況でのプログラムの動作は完全に予測不可能になり、おそらく重大な損失につながります。

さらに、グローバル状態はコードの可読性を損ないます。コードに明示的に導入されていない外部依存関係がある場合、コードのメンテナンスを担当する人は誰でもコードを探して、どこから来たかを把握する必要があります。

代替手段が存在するかどうかについては、グローバルステートをまったく持たないことは不可能ですが、実際には、グローバルステートを他のすべてをラップする単一のオブジェクトに制限することは通常可能であり、スコープルールに依存して参照されることはありません使用している言語の。特定のオブジェクトが特定の状態を必要とする場合は、コンストラクターに引数として渡すか、セッターメソッドによって明示的に要求する必要があります。これは、依存性注入と呼ばれます。

使用している言語のスコーピングルールにより、既にアクセスできる状態の一部を渡すのはばかげているように思えるかもしれませんが、その利点は膨大です。誰かがコードを単独で見ると、どの状態が必要で、どこから来たのかが明確になります。また、コードモジュールの柔軟性に関して大きな利点があり、異なるコンテキストで再利用する機会もあります。状態が渡され、状態への変更がコードブロックに対してローカルである場合、適切な状態(正しいデータ型の場合)を渡して、コードに処理させることができます。このスタイルで記述されたコードは、簡単に交換できる疎結合コンポーネントのコレクションのように見える傾向があります。モジュールのコードは、状態がどこから来たのかを気にするべきではなく、それをどのように処理するかだけです。

状態を渡すことがグローバルな状態に依存するよりもはるかに優れている理由は他にもたくさんあります。この答えは決して包括的なものではありません。おそらく、グローバルな状態が悪い理由についての本全体を書くことができます。


61
基本的に、誰もが状態を変更できるため、それに依存することはできません。
Oded

21
@Truth代替案?依存性注入
rdlowrey

12
あなたの主な議論は、設定を表すオブジェクトなど、事実上読み取り専用のものには当てはまりません。
フランク

53
いずれも読み取り専用グローバル変数が悪い理由を説明していません…OPが明示的に尋ねました。それはそうでなければ良い答えです、私は彼が明示的に別のポイントに対処したときにOPが「受け入れられた」とマークしたことに驚いています。
コンラッドルドルフ

20
being able to unit test code is a major step in the process of proving its correctness (or at least fitness for purpose)。いいえ、そうではありません。「プログラムテストがバグの存在を説得力を持って実証するかもしれないが、バグの不在を実証することはできないと指摘されてから20年になります。過去の錬金術師のように、彼のテスト戦略を改良するために、クリソコスミックな精製を改良し続けました。」-1988年、ジクストラ(それは今から40年になります...)
メイソンウィーラー

135

可変グローバル状態は多くの理由で悪です:

  • 可変グローバル状態からのバグ -多くのトリッキーなバグは、可変性によって引き起こされます。プログラムのどこからでも突然変異によって引き起こされる可能性のあるバグは、より正確です。多くの場合、正確な原因を突き止めるのが難しいためです。
  • 貧弱なテスタビリティ -可変のグローバル状態がある場合、記述するテスト用にそれを構成する必要があります。これにより、テストが難しくなります(したがって、人である人はテストを行う可能性が低くなります!)。たとえば、アプリケーション全体のデータベース資格情報の場合、あるテストが他のすべてとは異なる特定のテストデータベースにアクセスする必要がある場合はどうなりますか?
  • 柔軟性 -コードの一部がグローバル状態の1つの値を必要とするが、別の部分が別の値を必要とする場合(トランザクション中の一時的な値など)あなたは突然あなたの手に厄介なリファクタリングを持っています
  • 関数の不純物 -「純粋な」関数(つまり、結果が入力パラメーターのみに依存し、副作用がない関数)は、より大きなプログラムを構築するために推論および構成するのがはるかに簡単です。可変グローバル状態を読み取りまたは操作する関数は、本質的に不純です。
  • コードの理解 -多くの可変グローバル変数に依存するコードの動作は理解するのがはるかに難しい-コードの動作について推論する前に、グローバル変数との可能な相互作用の範囲を理解する必要があります。状況によっては、この問題は扱いにくいものになります。
  • 同時実行性の問題 -可変のグローバル状態は、通常、同時実行の状況で使用される場合、何らかの形式のロックを必要とします。これを正しく行うのは非常に難しく(バグの原因です)、コードをかなり複雑にします(保守が難しく/高価です)。
  • パフォーマンス -複数のスレッドが同じグローバル状態で継続的にバッシングすると、キャッシュの競合が発生し、システム全体の速度が低下します。

可変グローバル状態の代替:

  • 関数パラメーター -多くの場合見落とされますが、関数をより適切にパラメーター化することがグローバルな状態を回避するための最良の方法です。重要な概念上の質問を解決することを強制します:この機能がその仕事をするために必要な情報は何ですか?すべての関連情報をラップする一連の関数に渡すことができる「コンテキスト」と呼ばれるデータ構造を持つことが理にかなっている場合があります。
  • 依存性注入 -関数パラメーターと同じ、少し前に(関数呼び出しではなくオブジェクト構築で)行われます。ただし、依存関係が可変オブジェクトである場合は注意してください。これにより、可変グローバル状態と同じ問題がすぐに発生する可能性があります。
  • 不変のグローバル状態はほとんど無害です-事実上定数です。しかし、それが本当に一定であること、そして後でそれを可変のグローバル状態に変えたいと思わないようにしてください!
  • 不変のシングルトン -不変のグローバル状態とほとんど同じですが、必要になるまでインスタンス化を延期できます。高価な1回限りの事前計算が必要な大規模な固定データ構造などに役立ちます。可変シングルトンはもちろん可変グローバル状態と同等であり、したがって悪です:-)
  • 動的バインディング -Common Lisp / Clojureなどの一部の言語でのみ使用できますが、これにより、他のスレッドに影響を与えない、制御されたスコープ(通常はスレッドローカルベース)内の値を効果的にバインドできます。現在の実行スレッドのみが影響を受けることがわかっているため、これはグローバル変数と同じ効果を得る「安全な」方法です。これは、たとえば、それぞれが独立したトランザクションを処理する複数のスレッドがある場合に特に役立ちます。

11
コンテキストが可変の場合、関数パラメーターまたは依存性注入のいずれかでコンテキストオブジェクトを渡すと、可変グローバル状態を使用するのと同じ問題が発生すると思います。
アルフレードオソリオ

1
すべての良いもの、そしてアーメン!しかし、問題は、不変のグローバルな状態についてです
MarkJ

@Alfredo-非常に本当です。そうではありませんけれどもとして、少なくともスコープが多少制御することができるので、悪いです。しかし、一般的には、コンテキストと依存関係を不変にすることで問題を解決する方が簡単です。
ミケラ

2
可変/不変の場合は+1。不変のグローバルは問題ありません。遅延ロードされていても変更されないものです。もちろん、グローバル変数を公開するのではなく、グローバルインターフェイスまたはAPIを公開します。
ジェス14年

1
@giorgio問題は、問題の変数が起動時に値を取得し、プログラム実行中にその後変更されないことを明確にします(システムフォルダー、データベース資格情報)。つまり、不変であり、値が与えられると変更されません。個人的には、「状態」という言葉も使用します。これは、実行ごとに、または異なるマシン上で異なる可能性があるためです。より良い言葉があるかもしれません。
MarkJ

62
  1. いまいましいアプリ全体がそれを使用できるので、それらを再度ファクタリングすることは常に信じられないほど困難です。グローバルに関係する何かを変更した場合、すべてのコードを変更する必要があります。これはメンテナンスの頭痛の種です。単にgrepタイプ名がどの関数を使用しているかを知ることができるだけではありません。
  2. 隠れた依存関係を導入し、マルチスレッドを破壊し、ますます多くのアプリケーションにとってますます重要になっているため、これらは悪いです。
  3. グローバル変数の状態は常に完全に信頼できません。これは、すべてのコードがそれに対して何かをしている可能性があるためです。
  4. テストするのは本当に難しいです。
  5. APIの呼び出しが難しくなります。「あなたは、APIを呼び出す前にSET_MAGIC_VARIABLE()を呼び出すために忘れてはならない」ばかりされて物乞い誰かがそれを呼び出すことを忘れため。APIを使用するとエラーが発生しやすくなり、見つけにくいバグが発生します。通常のパラメーターとして使用することにより、呼び出し元に値を適切に提供するように強制します。

参照を必要とする関数に参照を渡すだけです。そんなに難しくありません。


3
さて、ロックをカプセル化するグローバル構成クラスを持つことができ、可能なときにいつでも状態を変更するように設計されています。コード内の1000倍の場所から構成リーダーをインスタンス化するよりも、このアプローチを選択します。しかし、はい、予測不可能性はそれらについての絶対に最悪のことです。
コーダー

6
@Coder:グローバルに代わる健全な代替手段は「コード内の1000xの場所からの構成リーダー」ではなく、構成オブジェクトを作成し、メソッドがパラメーターとして受け入れることができる1つの構成リーダーです(->依存性注入)。
-sleske

2
Nitpicking:グローバルよりもタイプをgrepする方が簡単なのはなぜですか?質問は読み取り専用グローバルに関するものなので、ポイント2と3は関係ありません
-MarkJ

2
@MarkJ:誰も名前を隠したことはないと思います。
DeadMG

2
@ DeadMG、Re "..は常に完全に信頼できない.."、依存関係の注入についても同じことが言えます。パラメーターにしたからといって、objの状態が固定されていることが保証されるわけではありません。関数の下のどこかで、注入された変数の状態を変更する別の関数を呼び出すことができます。
パセリエ

34

「状態」と言うと、通常は「可変状態」を意味します。また、グローバルな可変状態は、プログラムのどの部分も他の部分に影響を与える可能性があるため(グローバル状態を変更することにより)、完全に悪です。

不明なプログラムのデバッグを想像してください。関数Aは特定の入力パラメーターに対して特定の動作をすることがわかりますが、同じパラメーターに対しては異なる動作をすることがあります。グローバル変数xを使用していることがわかります。

xを変更する場所を探し、それを変更する場所が5つあることがわかります。これで、関数Aが何をするのかを知ることができます...


だから、不変のグローバル状態はそれほど悪ではないのですか?
FrustratedWithFormsDesigner

50
不変のグローバル状態は、「定数」として知られているよく知られたグッドプラクティスであると言えます。
テラスティン

6
不変のグローバル状態は悪ではなく、ただ悪いだけです:-)。導入するカップリング(変更、再利用、単体テストを難しくする)のために依然として問題がありますが、作成される問題ははるかに少ないので、単純なケースでは通常許容されます。
-sleske

2
IFFはグローバル変数を使用するため、1つのコードのみで変更する必要があります。残りは自由に読むことができます。それを変更する他の人の問題は、カプセル化とアクセス機能ではなくなりません。これは、これらの構成要素の目的ではありません。
-phkahler

1
@Pacerier:はい、グローバル変数として使用するかローカル変数として使用するかに関係なく、広く使用されているインターフェイスを変更することは困難です。しかし、それは私のポイントとは無関係です。つまり、異なるコードの相互作用をグローバル変数で理解するのは難しいということです。
sleske

9

あなた自身の質問に答えました。それらは「乱用」されると管理が難しくなりますが、適切に使用されると、それらを封じ込める方法を知っている誰かによって有用であり、[ある程度]予測可能です。グローバルの保守および変更は通常悪夢であり、アプリケーションのサイズが大きくなるにつれて悪化します。

グローバルが唯一の選択肢であり、簡単な修正であるという違いを理解できる経験豊富なプログラマーは、それらを使用することで最小限の問題を抱えることできます。しかし、それらの使用で生じる可能性のある無限の問題は、それらを使用することに対するアドバイスを必要とします。

編集:私が意味することを明確にするために、グローバルは本質的に予測不可能です。予測不可能なものと同様に、予測不能を封じ込めるための措置を講じることができますが、実行できることには常に制限があります。これに加えて、プロジェクトに参加する新しい開発者の手間が比較的未知の変数に対処する必要があるため、グローバルの使用に対する推奨事項は理解できるはずです。


9

シングルトンには多くの問題があります-ここに私の頭の中の2つの最大の問題があります。

  • 単体テストに問題が生じます。グローバルな状態は、あるテストから次のテストへと汚染される可能性があります

  • これは、「1つだけの」ハードルールを実施します。これは、変更することはできませんでしたが、突然行われます。次に、グローバルにアクセス可能なオブジェクトを使用したユーティリティコード全体を変更する必要があります。

そうは言っても、ほとんどのシステムにはビッググローバルオブジェクトが必要です。これらは、大きくて高価なアイテム(データベース接続マネージャーなど)、または広範な状態情報(ロック情報など)を保持します。

シングルトンに代わる方法は、起動時にこれらのビッググローバルオブジェクトを作成し、このオブジェクトへのアクセスを必要とするすべてのクラスまたはメソッドにパラメーターとして渡すことです。

ここでの問題は、「小包を渡す」という大きなゲームになってしまうことです。コンポーネントとその依存関係のグラフがあり、一部のクラスは他のクラスを作成し、それぞれが生成されたコンポーネント(または生成されたコンポーネントのコンポーネント)が必要とするため、それぞれが依存関係コンポーネントの束を保持する必要があります。

新しいメンテナンスの問題が発生します。例:突然、グラフの奥深くにある「WidgetFactory」コンポーネントには、モックアウトするタイマーオブジェクトが必要です。ただし、「WidgetFactory」は「WidgetCreationManager」の一部である「WidgetBuilder」によって作成され、1つだけが実際に使用する場合でも、このタイマーオブジェクトを認識する3つのクラスが必要です。あきらめて、シングルトンに戻り、このタイマーオブジェクトをグローバルにアクセス可能にしたいだけです。

幸いなことに、これはまさにDependency Injectionフレームワークによって解決される問題です。作成する必要のあるクラスをフレームワークに伝えるだけで、リフレクションを使用して依存関係グラフを把握し、必要なときに各オブジェクトを自動的に構築します。

したがって、要約すると、シングルトンは不良であり、代替手段は依存性注入フレームワークを使用することです。

私はたまたまCastle Windsorを使用していますが、あなたは選択に甘やかされています。参照このページを利用できるフレームワークのリストについては、2008年に戻ってから。


8

まず、依存性注入を「ステートフル」にするには、シングルトンを使用する必要があります。そのため、これは何らかの方法で代替手段であると誤解されています。人々は常にグローバルコンテキストオブジェクトを使用します...たとえばセッション状態でさえ、本質的にグローバル変数です。依存性注入によるかどうかにかかわらず、すべてを渡すことが常に最良の解決策とは限りません。私は現在、多くのグローバルコンテキストオブジェクト(IoCコンテナーを介して挿入されたシングルトン)を使用する非常に大規模なアプリケーションに取り組んでおり、デバッグするのに問題はありませんでした。特に、イベント駆動型アーキテクチャでは、変更されたものをすべて渡すのではなく、グローバルコンテキストオブジェクトを使用することをお勧めします。誰に頼るかによります。

あらゆるものが悪用される可能性があり、アプリケーションの種類にも依存します。Webアプリで静的変数を使用することは、デスクトップアプリとはまったく異なります。グローバル変数を回避できる場合は、回避しますが、場合によっては使用することもあります。少なくとも、グローバルデータが明確なコンテキストオブジェクトにあることを確認してください。デバッグに関しては、呼び出しスタックと一部のブレークポイントでは解決できません。

グローバル変数を盲目的に使用するのは悪い考えであることを強調したいと思います。関数は再利用可能であるべきであり、データの出所を気にしないでください-グローバル変数を参照すると、関数と特定のデータ入力が結合されます。これが渡されるべきであり、依存性注入が役立つことがある理由です。ただし、まだ集中型コンテキストストアを処理しています(シングルトン経由)。

ところで... Linqの作成者を含む一部の人々は依存性注入が悪いと考えていますが、それは私を含め、人々がそれを使用するのを止めるつもりはありません。最終的に経験はあなたの最高の教師になります。ルールに従う時と、それらを破る時があります。


4

ここで他のいくつかの答えが可変グローバル状態と不変グローバル状態を区別するので、不変グローバル変数/設定さえ煩わしいことが多いと意見を述べたいと思います

質問を考えてください:

...たとえば、システムフォルダパス、またはアプリケーション全体のデータベース資格情報など、アプリケーションのグローバル構成が必要だとしましょう。...

確かに、小さなプログラムの場合、これはおそらく問題ではありませんが、少し大きなシステムのコンポーネントでこれを行うと、すべてのテスト(〜が同じプロセス内で実行されている)同じグローバル構成値。

すべての構成データが明示的に渡されると、コンポーネントのテストがはるかに簡単になり、複数のテストのグローバル構成値をブートストラップする方法を心配する必要がなくなります。


3

1つは、シングルトンで実行できる問題とまったく同じ問題に遭遇する可能性があります。今日の「私が必要なのはグローバルなもの」のように見えるものが、突然、もっと必要なものに変わります。

たとえば、システム全体に対して1つのグローバル構成が必要なため、今日、このグローバル構成システムを作成します。数年後、あなたは別のシステムに移植し、誰かが「ねえ、あなたは知っている、これは1つの一般的なグローバル設定と1つのプラットフォーム固有の設定があればうまくいくかもしれない」と言います。突然、グローバル構造をグローバルではないものにするためのすべての作業が完了したので、複数の構造を持つことができます。

(これはランダムな例ではありません...これは、私が現在いるプロジェクトの構成システムで起こりました。)

非グローバルなものを作成するコストは通常​​些細なものであると考えると、そうするのは愚かなことです。あなたは将来の問題を作成しているだけです。


あなたの例はOPが意味するものではありません。プログラムの実行中のインスタンスのすべてのキーを構成マネージャークラスの複数のインスタンスに分割するため、抽象構成(アプリケーション固有の設定キーなしの構成マネージャーデータ構造のみ)を複数回インスタンス化することを明確に言っています。(たとえば、そのようなインスタンスはそれぞれ1つの構成ファイルを処理できます)。
ジョーSo

3

不思議なことに、もう1つの問題は、アプリケーションが「グローバル」では不十分なため、アプリケーションのスケーリングが困難になることです。グローバル変数のスコープはプロセスです。

複数のプロセスを使用するか、複数のサーバーで実行することにより、アプリケーションをスケールアップすることはできません。少なくとも、すべてのグローバルを除外して、他のメカニズムに置き換えるまでは。


3

なぜグローバル国家はそれほど悪なのか?

可変グローバル状態は悪です。なぜなら、脳が一度に数個以上のパラメーターを考慮し、それらを組み合わせてタイミングの観点と値の観点の両方から何かに影響を与えるのは難しいからです。

そのため、プログラムの実行中に変更されるいくつかの外部的な理由がある動作を持つオブジェクトのデバッグやテストは非常に苦手です。何十ものこれらのオブジェクトをまとめて考える必要があるときはもちろんです。


3

安全で堅牢なシステム設計のために設計された言語は、多くの場合、グローバルな可変状態を完全に取り除きます。(ほぼ間違いなく、これはグローバルがないことを意味します。なぜなら、不変オブジェクトは、ある意味ではステート遷移を持たないため、実際にはステートフルではないからです。)

Joe-Eはその一例です。DavidWagnerはこのように決定を説明しています。

オブジェクトへのアクセス権を持つユーザーの分析と最小権限の原則は、機能がグローバル変数に格納されると破壊され、プログラムのどの部分からも読み取り可能になる可能性があります。オブジェクトがグローバルに利用可能になると、分析範囲を制限することはできなくなります。オブジェクトへのアクセスは、プログラム内のコードから差し控えることができない特権です。Joe-Eは、グローバルスコープに機能がなく、不変データのみが含まれていることを確認することにより、これらの問題を回避します。

それについて考える1つの方法は

  1. プログラミングは分散推論の問題です。大規模プロジェクトのプログラマーは、プログラムを個々の部分に分けられるように分割する必要があります。
  2. スコープが小さいほど、推論が容易になります。これは、システムのプロパティを証明しようとする個人および静的分析ツールの両方、およびシステムのプロパティをテストする必要があるテストの両方に当てはまります。
  3. グローバルに利用可能な重要な権限のソースは、システムのプロパティについて推論するのを困難にします。

したがって、グローバルに変更可能な状態により、

  1. 堅牢なシステムの設計、
  2. システムの特性を証明するのが難しく、そして
  3. テストが実稼働環境と同様の範囲でテストされていることを確認するのが難しくなります。

グローバルな可変状態は、DLLの地獄に似ています。時間の経過とともに、大規模システムのさまざまな部分は、可変状態の共有部分とは微妙に異なる動作を必要とします。DLLの地獄と共有可能な可変状態の不整合を解決するには、異なるチーム間で大規模な調整が必要です。これらの問題は、グローバルステートが最初から適切にスコープされていれば発生しません。


2

グローバルはそれほど悪くはありません。他のいくつかの回答で述べたように、それらの本当の問題は、今日のグローバルフォルダーパスが、明日、数個または数百個のいずれかになることです。簡単な1回限りのプログラムを書いている場合は、簡単であればグローバルを使用してください。ただし、一般に、必要なのは1つだけだと思っている場合でも複数を許可するのがよいでしょう。突然2つのデータベースと通信する必要がある大規模で複雑なプログラムを再構築しなければならないのは気持ちの悪いことです。

しかし、それらは信頼性を傷つけません。どれもが予期せず変更された場合、プログラムの多くの場所から参照されるデータは、問題を引き起こす可能性があります。列挙子は、列挙中のコレクションが列挙の途中で変更されると停止します。イベントキューイベントは、互いにトリックを行うことができます。スレッドは常に大混乱を招く可能性があります。ローカル変数でも変更不可能なフィールドでもないものはすべて問題です。グローバルはこの種の問題ですが、グローバルではないことでそれを修正するつもりはありません。

ファイルに書き込もうとしてフォルダパスが変更された場合、変更と書き込みを同期する必要があります。(うまくいかない可能性のあるものの1つとして、パスを取得すると、そのディレクトリが削除され、フォルダーパスが適切なディレクトリに変更され、削除されたディレクトリに書き込もうとします。)フォルダーパスはグローバルであるか、プログラムが現在使用している1000の1つです。

キューのさまざまなイベント、さまざまなレベルの再帰、またはさまざまなスレッドによってアクセスできるフィールドには、実際の問題があります。単純(かつ単純化)にするには、ローカル変数は適切でフィールドは悪いです。しかし、以前のグローバルは依然としてフィールドであるため、この(非常に重要ですが)問題はグローバルフィールドの良い状態または悪い状態には適用されませ

追加:マルチスレッドの問題:

(イベントキューや再帰呼び出しでも同様の問題が発生する可能性がありますが、マルチスレッドは最悪です。)次のコードを検討してください。

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

場合はfilePath、ローカル変数や定数のいくつかの種類である、あなたのプログラムがされていないので、実行するときに失敗するつもりはfilePathnullです。チェックは常に機能します。他のスレッドはその値を変更できません。 それ以外の場合、保証はありません。Javaでマルチスレッドプログラムを書き始めたとき、このような行でNullPointerExceptionsが常に発生していました。 どれか他のスレッドはいつでも値を変更できますが、頻繁に変更します。他のいくつかの回答が指摘しているように、これはテストに深刻な問題を引き起こします。上記のステートメントは数十億回動作し、広範囲にわたる包括的なテストを経て、本番環境で一度爆発します。ユーザーは問題を再現することができず、物事を見ていると忘れてしまったと確信するまで、それは二度と起こりません。

グローバルには間違いなくこの問題があり、それらを完全に削除するか、定数またはローカル変数に置き換えることができる場合、それは非常に良いことです。Webサーバーでステートレスコードを実行している場合は、おそらく可能です。通常、すべてのマルチスレッドの問題はデータベースで処理できます。

しかし、あるユーザーアクションから次のユーザーアクションまでをプログラムで記憶する必要がある場合、実行中のスレッドからアクセス可能なフィールドがあります。グローバルをそのような非グローバルフィールドに切り替えても、信頼性は向上しません。


1
これが何を意味するのかを明確にできますか?:「ローカル変数でも変更不可能なフィールドでもないものは問題です。グローバルはこの種の問題ですが、非グローバルにすることで修正するつもりはありません。」
アンドレスF.

1
@AndresF .:答えを拡張しました。私は、このページのほとんどの人がデータベースを使用したサーバーコードに近いデスクトップアプローチを取っていると思います。これらの場合、「グローバル」とは異なることを意味する場合があります。
-RalphChapin

2

実際のアプリケーションでは、状態は避けられません。好きなようにまとめることができますが、スプレッドシートのセルにデータを含める必要があります。インターフェイスとしての機能のみを持つセルオブジェクトを作成できますが、セルのメソッドを呼び出してデータを変更できる場所の数は制限されません。オブジェクト階層全体を構築して、インターフェイスを非表示にしてコードの他の部分ができないようにしますデフォルトでデータを変更します。それは、包含オブジェクトへの参照が勝手に渡されることを妨げません。同時実行の問題をそれ自体で排除するものもありません。データへのアクセスを拡散することは難しくなりますが、実際にグローバルで認識されている問題を排除するわけではありません。誰かが状態の一部を変更したい場合、それはグローバルであるか複雑なAPIを介して行われます(後者は防止するのではなく、落胆させるだけです)。

グローバルストレージを使用しない真の理由は、名前の衝突を避けるためです。同じグローバル名を宣言する複数のモジュールをロードすると、未定義の動作(単体テストに合格するためデバッグが非常に困難)またはリンカーエラー(C-リンカーはこれについて警告または失敗しますか?)

コードを再利用する場合は、別の場所からモジュールを取得し、同じ名前のモジュールを使用したために誤ってグローバルに踏み込まないようにする必要があります。または、幸運でエラーが発生した場合、衝突を防ぐためにコードの1つのセクションのすべての参照を変更する必要はありません。


1

すべてのグローバル状態を簡単に確認してアクセスできる場合、プログラマーは常にそうすることになります。暗黙のうちに依存関係を追跡することは困難です(int blahblahは、配列fooが何でも有効であることを意味します)。基本的に、すべてを個別にいじることができるため、プログラムの不変条件を維持することはほぼ不可能になります。someIntはotherIntと関係があり、管理が難しく、いつでも直接変更できるかどうかを証明するのは困難です。

とはいえ、それは実行できます(一部のシステムで唯一の方法だった頃)、しかしそれらのスキルは失われます。それらは主にコーディングと命名規則を中心に展開します-フィールドは正当な理由で移動しました。コンパイラとリンカは、人間がマスタープランに従ってソースを読み取ることに依存するよりも、クラス/モジュールの保護/プライベートデータの不変条件をより適切にチェックします。


1
「しかし、それらのスキルは失われています」...まだ完全ではありません。私は最近、「クラリオン」に誓うソフトウェアハウスで働いていました。「クラリオン」は、サブルーチンに引数を渡すなどの機能を持たない独自の基本的な言語を備えたコード生成ツールです。 「変化」または「近代化」、最終的に私の発言にうんざりし、私が不十分で無能であると描写しました。私は去らなければなりませんでした
ルイ・サマーズ

1

グローバル変数が良いか悪いかはわかりませんが、議論に追加するのは、グローバル状態を使用していない場合、おそらく多くのメモリを浪費しているという事実を伝えることです。特に、クラスを使用してフィールドに依存関係を保存する場合。

グローバル状態の場合、そのような問題はなく、すべてがグローバルです。

たとえば、次のシナリオを想像してください。クラス「Board」と「Tile」で構成される10x10グリッドがあります。

OOPの方法でそれを行いたい場合は、おそらく「Board」オブジェクトを各「Tile」に渡します。「タイル」には、座標を格納する2つの「バイト」タイプのフィールドがあるとしましょう。32ビットマシンで1タイルに必要な合計メモリは(1 + 1 + 4 = 6)バイトになります。x座標に1、y座標に1、ボードへのポインターに4です。これにより、10x10タイルのセットアップで合計600バイトが得られます

ボードがグローバルスコープにある場合、各タイルからアクセス可能な単一のオブジェクトは、タイルごとに2バイトのメモリ、つまりxおよびy座標バイトを取得するだけで済みます。これにより、200バイトのみが得られます。

したがって、この場合、グローバル状態のみを使用すると、メモリ使用量の1/3を取得できます。

これは、他のものに加えて、グローバルスコープが(比較的)C ++のような低レベル言語のままである理由だと思います


1
それは言語に大きく依存しています。プログラムが永続的に実行される言語では、多くのクラスをインスタンス化する代わりにグローバルスペースとシングルトンを使用してメモリを節約できるのは事実かもしれませんが、その議論でさえも不安定です。優先事項がすべてです。PHP(要求ごとに1回実行され、オブジェクトが保持されない)のような言語では、その引数でさえ意味がありません。
マダラ14

1
@MadaraUchihaいいえ、この議論は決して不安定ではありません。VMやその他のコンパイル済み言語がこの種の問題でハードコアなコード最適化を行わない限り、それは客観的な事実です。そうでなければ、それはまだ関連しています。「ワンショット」であるサーバー側プログラムでも、実行中はメモリが確保されます。高負荷サーバーでは、これが重要なポイントになる場合があります。
luke1985

0

グローバル状態で考慮すべきいくつかの要因があります。

  1. プログラマーのメモリ空間
  2. 不変のグローバル/一度だけ書き込み可能なグローバル。
  3. 可変グローバル
  4. グローバルへの依存。

グローバルが多いほど、重複が発生する可能性が高くなり、重複が同期しなくなると問題が発生します。人間の記憶にすべてのグローバルを保持することは、必要であり苦痛でもあります。

通常、不変/書き込みは1回で問題ありませんが、初期化シーケンスエラーには注意してください。

可変グローバルはしばしば不変グローバルと間違われます…

グローバルを効果的に使用する関数には、余分な「非表示」パラメーターがあり、リファクタリングが難しくなります。

グローバルな状態は悪ではありませんが、確かにコストがかかります。利益がコストを上回る場合に使用します。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.