グローバルとデータベースとの違いは何ですか?


250

私はちょうどこの古い質問に出くわし、グローバル状態の悪さを問いかけました。トップ投票で受け入れられた答えは、グローバル変数で動作するコードを信頼できないと断言しています。その後、値とデータが異なるため、あなたのコードの動作がどうなるか分かりません! しかし、それを見たとき、私はそれが本当に弱い説明だと思わざるを得ません。データベースに保存されたデータを操作するのとはどう違うのですか?

プログラムがデータベースのデータを処理しているとき、システムの他のコードがそれを変更しているのか、まったく別のプログラムがそれを変更しているのかは関係ありません。データが何であるかは気にしません。それが全体のポイントです。重要なのは、コードが遭遇するデータを正しく処理することです。(明らかに、私はここでしばしば厄介なキャッシングの問題を明らかにしていますが、今のところそれを無視しましょう。)

ただし、使用しているデータが、データベース(またはユーザー入力、ネットワークソケット、ファイルなど)など、コードで制御できない外部ソースからのものであり、何も問題がない場合それで、コード自体のグローバルデータはどうですか?あなたのプログラムはそれをはるかに高度に制御していますが、誰も問題とみなさない完全に通常のものより明らかに悪いものではない場合、悪いことですか?


117
ベテランのメンバーがドグマに少し挑戦するのを見るのは良いことです
...-svidgen

10
アプリケーションでは、通常、データベースにアクセスする手段を提供します。この手段は、データベースにアクセスしたい関数に渡されます。グローバル変数でそれを行うのではなく、それらが手元にあることを知っているだけです。それはまさにそこの重要な違いです。
アンディ

45
グローバル状態は、任意の数のアプリケーションが同時にアクセスする無限に多くの列を持つ単一行を持つ単一テーブルを持つ単一データベースを持つようなものです。
BevynQ

42
データベースも悪です。
スティグヘマー

27
ここで行った議論を「反転」させ、他の方向に進むのは楽しいことです。別の構造体へのポインターを持つ構造体は、論理的には、別のテーブルの別の行にキーを設定する1つのテーブルの1つの行にある外部キーです。データベース内のデータを操作することとは異なるリンクリストを含むコードをどのように使用していますか?回答:そうではありません。質問:そのような異なるツールを使用して、インメモリデータ構造とデータベース内データ構造を操作するのはなぜですか?回答:私は本当に知りません!良いデザインというよりは歴史の偶然のようです。
エリックリッパー

回答:


118

まず、あなたがリンクする答えはその特定の問題を誇張しており、グローバル状態の主な悪は、将来のシステムの動作を変更することを困難にする可能性のある予測不可能な方法でカップリングを導入するということです。

しかし、この問題をさらに詳しく調べると、典型的なオブジェクト指向アプリケーションのグローバル状態とデータベースに保持されている状態には違いがあります。簡単に言うと、これらのうち最も重要なものは次のとおりです。

  • オブジェクト指向システムでは、元の型のサブタイプである限り、オブジェクトを別のクラスのオブジェクトに置き換えることができます。これにより、データだけでなく動作を変更できます

  • 通常、アプリケーションのグローバル状態は、データベースが提供するような強力な一貫性の保証を提供しません。その間、一貫した状態を確認するトランザクションはなく、アトミック更新などもありません。

さらに、データベースの状態は必要な悪とみなすことができます。システムから削除することはできません。ただし、グローバル状態は不要です。完全に排除できます。したがって、データベースの問題が同じくらいひどい場合でも、潜在的な問題の一部を排除でき、部分的な解決策は解決策がないよりも優れています。


44
一貫性のポイントが実際には主な理由だと思います:グローバル変数がコードで使用されるとき、それらが実際にいつ初期化されるのか通常はわかりません。モジュール間の依存関係は呼び出しシーケンス内に深く隠されており、2つの呼び出しをスワップするような単純なものは、最初に使用されたときに突然グローバル変数が正しく初期化されないため、非常に厄介なバグを生み出す可能性があります。少なくともそれは、私が扱う必要のあるレガシーコードに伴う問題であり、リファクタリングが悪夢になります。
cmaster

24
@DavidHammen私は実際にオンラインゲームのワールドステートシミュレーションに取り組んできました。これは明らかにあなたが話しているアプリケーションのカテゴリに属します。そこでもグローバルステートを使用しません(そしてしませんでした)。グローバル状態を使用することで効率がある程度向上したとしても、問題はグローバル状態がスケーラブルではないことです。シングルスレッドアーキテクチャからマルチスレッドアーキテクチャに移行すると、使用が難しくなります。NUMAアーキテクチャに移行すると効率悪くなります。それはなっ不可能あなたが分散アーキテクチャに移動したとき。あなたは...から日付を引用論文
ジュール・

24
1993。これらの問題は、当時はそれほど問題ではありませんでした。著者は、1,000個のオブジェクトの相互作用をシミュレートするシングルプロセッサシステムに取り組んでいました。最近のシステムでは、少なくともデュアルコアシステムでその種のシミュレーションを実行する可能性がありますが、1つのシステムで少なくとも6コアになる可能性があります。さらに大きな問題については、クラスターで実行します。このような変更では、グローバル状態を効果的に共有できないため、グローバル状態を回避する必要があります。
ジュール

19
データベースの状態を「必要な悪」と呼ぶのは少しばかりです。つまり、いつから国家は悪になったのでしょうか?状態はデータベースの全体的な目的です。状態は情報です。状態がない場合、所有しているのは演算子のみです。何も操作する必要のないオペレーターは何がいいですか?その状態はどこかに行かなければなりません。結局のところ、関数型プログラミングは目的を達成するための手段にすぎず、変化する状態がなければ、何もする意味がありません。ケーキを必要な悪と呼ぶパン屋のようなものです-悪ではありません。それは事の全体のポイントです。
J ...

5
@DavidHammen「ゲーム内のすべてのオブジェクトについて少なくとも少し知っているオブジェクトがまだあります」必ずしもそうではありません。現代の分散シミュレーションの主な手法は、局所性を利用して、遠くのオブジェクトが遠くのものすべてを知る必要はなく、それらの遠くのオブジェクトの所有者によって提供されるデータのみを知るように近似することです。
JAB

75

まず、リンクした質問に対する受け入れられた回答に基づいて、グローバル変数の問題は何ですか?

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

データベースは、ほとんどの場合、ACIDに準拠しています。ACIDは、データストアを予測不能または信頼性の低いものにする根本的な問題に特に対処します。

さらに、グローバル状態はコードの可読性を損ないます。

これは、グローバル変数が、その使用法から遠く離れたスコープに存在するためです。別のファイルに存在する場合もあります。データベースを使用するときは、読み取り中のコードに対してローカルなレコードセットまたはORMオブジェクトを使用しています(またはそうすべきです)。

通常、データベースドライバーは、問題のドメインに関係なく同じデータにアクセスするための一貫したわかりやすいインターフェイスを提供します。データベースからデータを取得すると、プログラムにはデータのコピーがあります。更新はアトミックです。グローバル変数とは対照的に、同期を自分で追加しない限り、複数のスレッドまたはメソッドが同じデータ上でアトミック性なしで動作している場合があります。データの更新は予測不可能であり、追跡が困難です。更新はインターリーブされる可能性があり、マルチスレッドデータ破損の例(インターリーブされた増分など)の沼地標準テキストを引き起こします。

データベースは通常、最初はグローバル変数とは異なるデータをモデル化しますが、それはさておき、データベースは、グローバル変数に関する多くの懸念を軽減するACID準拠のデータストアとして設計されています。


4
+1あなたが言っているのは、データベースにはトランザクションがあり、複数のグローバルステートをアトミックに読み書きできるということです。良い点。完全に独立した情報ごとにグローバル変数を使用することによってのみ回避できます。
l0b0

1
@ l0b0トランザクションは、ほとんどのACID目標を達成するメカニズムです。ただし、DBインターフェース自体は、データをよりローカルなスコープにすることでコードを明確にします。try-with-resourcesブロックでJDBC RecordSetを使用するか、単一の関数呼び出しを使用してデータの一部を取得するORM関数を使用することを考えてください。これを、どこかでグローバルに読んでいるコードから遠く離れたデータを管理することと比較してください。

1
そのため、関数の最初に値を(mutexを使用して)ローカル変数にコピーし、ローカル変数を変更してから、最後にグローバル変数に値をコピーする場合、グローバル変数を使用してもかまいません。関数?(...彼は修辞的に尋ねた。)
RM

1
@RM彼は2つのポイントに言及しました。あなたが投げたものは最初の(プログラム状態は予測不能)に対処するかもしれませんが、2番目(コードの可読性)には対処しません。実際、プログラムの可読性がさらに悪くなる可能性があります:P。
リウォーク

1
@RM関数は一貫して実行されます、はい。しかし、その間に他の何かがグローバル変数を変更したかどうかの質問があり、その変更はあなたが書いているものよりも重要でした。もちろん、データベースにも同じ問題があるかもしれません。
グラハム

45

私はいくつかの観察を提供します:

はい、データベースはグローバルな状態です。

実際、あなたが指摘したように、それは超グローバルな状態です。それは普遍的です!その範囲は伴うもの、誰のデータベースに接続します。そして、長年の経験を持つ多くの人々が、データ内の「奇妙なこと」が1つ以上の関連アプリケーションで「予期しない動作」につながったという恐ろしい話をあなたに伝えることができると思います...

グローバル変数を使用することの潜在的な結果の1つは、2つの異なる「モジュール」がその変数を独自の異なる目的で使用することです。そして、その範囲で、データベーステーブルは同じです。同じ問題の犠牲になる可能性があります。

うーん...ここにあるもの:

モジュールが何らかの方法で外部的に動作しない場合、何も実行されません。

有用なモジュールにはデータを指定するか、見つけることができます。また、データを返すことも状態を変更することもできます。しかし、何らかの方法で外部の世界と相互作用しない場合、何もしないかもしれません。

現在、私たちの好みはデータを受け取り、データを返すことです。ほとんどのモジュールは、外の世界が何をしているかをまったく無視して書くことができれば、簡単に書くことができます。しかし、最終的には、何かがする必要が見つけたデータをと変更、外部、グローバルな状態を。

さらに、実際のアプリケーションでは、データが存在するため、さまざまな操作でデータを読み取って更新できます。一部の問題は、ロックとトランザクションによって防止されます。ただし、原則としてこれらの操作が互いに競合するのを防ぐには、結局のところ、慎重に考える必要があります。(そして間違いを犯す...)

しかし、また、私たちは通常、グローバルな国家と直接連携ていません。

アプリケーションがデータ層(SQLなど)に存在しない限り、モジュールが動作するオブジェクトは、実際には共有グローバルステートのコピーです。実際の共有状態に影響を与えることなく、必要なことは何でもできます

そして、そのグローバル状態を変更する必要がある場合、与えられたデータが変更されていないという仮定の下で、通常、ローカルグローバルで行うのと同じようなロックを実行できます。

我々はよりそして最後に、我々は通常、データベースと異なることを行う可能性がありますいたずらグローバルで。

いたずらで壊れたグローバルは次のようになります。

Int32 counter = 0;

public someMethod() {
  for (counter = 0; counter < whatever; counter++) {
    // do other stuff.
  }
}

public otherMethod() {
  for (counter = 100; counter < whatever; counter--) {
    // do other stuff.
  }
}

私たちは、そのようなインプロセス/運用的なものにはデータベースを使用しません。そして、それはデータベースと私たちを抑止する単純な変数の相対的な利便性の遅い自然かもしれません:データベースと私たちの低迷、厄介な相互作用は、単にそれら作る悪い候補者たちは、歴史的に行われてきた過ちの多くのための変数。


3
データベースで「与えられたデータが変更されていないこと」を保証する方法(想定できないため)はトランザクションになります。
l0b0

はい...それは「同じようなロックの種類」で暗示されるはずでした。
svidgen

しかし、一日の終わりに慎重に考えることは難しい場合があります。

はい、データベースは確かにグローバルな状態です。そのため、gitやipfsのようなものを使用してデータを共有するのは魅力的です。
ウィリアムペイン

21

以下の基本的な主張に同意しません:

プログラムがデータベースのデータを処理しているとき、システムの他のコードがそれを変更しているのか、まったく別のプログラムがそれを変更しているのかは関係ありません。

私の最初の考えは「すごい。すごい」でした。これを正確に回避するために、多くの時間と労力が費やされ、各アプリケーションでどのようなトレードオフと妥協が機能するかを突き止めます。それをただ無視することは災害のレシピです。

しかし、建築レベルでも意見が一致します。グローバル変数は、単にグローバルな状態ではありません。どこからでも透過的にアクセスできるのはグローバルな状態です。データベースを使用するのとは対照的に、データベースへのハンドルが必要です-(グローバル変数にハンドルを格納する場合を除き...)

たとえば、グローバル変数を使用すると次のようになります

int looks_ok_but_isnt() {
  return global_int++;
}

int somewhere_else() {
  ...
  int v = looks_ok_but_isnt();
  ...
}

しかし、データベースで同じことを行うと、その実行内容をより明確にする必要があります

int looks_like_its_using_a_database( MyDB * db ) {
   return db->get_and_increment("v");
}

int somewhere_else( MyBD * db ) { 
   ...
   v = looks_like_its_using_a_database(db);
   ...
}

データベースは明らかにデータベースをいじっています。データベースを使用したくない場合は、明示的な状態を使用でき、データベースの場合とほとんど同じように見えます。

int looks_like_it_uses_explicit_state( MyState * state ) {
   return state->v++;
}


int somewhere_else( MyState * state ) { 
   ...
   v = looks_like_it_uses_explicit_state(state);
   ...
}

したがって、データベースを使用することは、グローバル変数を使用することよりも、明示的な状態を使用することにはるかに似ていると主張します。


2
ええ、OPが次のように言ったとき、私はそれが面白いと思った:「あなたはデータが何であるか気にしない;それが全体のポイントだ」-私たちが気にしないなら、なぜそれを保存するのか?ちょうど変数とデータの使用を停止してみましょう。ここで考えられるすべてでは。それは物事をはるかに簡単にするはずです。「世界を止めろ、降りたい!」

1
+1同じデータベースからの異なるスレッドまたはアプリの書き込みおよび読み取りは、多数のよく知られた問題の潜在的な原因です。そのため、データベースまたはアプリレベルで、これに対処するための戦略が常に必要です。両方。したがって、あなた(アプリ開発者)が他の誰がデータベースを読み書きしているのか気にしないのは間違いです。
アンドレスF.

1
+1補足として、この回答は、依存性注入に関して私が最も嫌いなことをほとんど説明しています。これらの種類の依存関係を隠します。
jpmc26

@ jpmc26私は単語をマークしているかもしれませんが、上記は依存性注入(グローバルルックアップとは対照的に)が依存性を明示的にする方法の良い例ではありませんか?おそらく、JAX-RSやSpringで使用されているアノテーションマジックのように、特定のAPIで問題を抱えているように思えます。
エミルルンドバーグ

2
@EmilLundbergいいえ、問題は階層がある場合です。依存性注入により、下位層の依存関係が上位層のコードから隠され、相互作用するものを追跡することが難しくなります。たとえば、にMakeNewThing依存しMakeNewThingInDb、コントローラクラスがを使用している場合MakeNewThing、データベースを変更していることはコントローラのコードからはわかりません。では、現在のトランザクションを実際にDB にコミットする別のクラスを使用するとどうなりますか?DIは、オブジェクトのスコープを制御することを非常に困難にします。
jpmc26

18

状態がどこか他の場所で変更される可能性があるため、グローバル変数が信頼できない唯一の理由は、それ自体、それらを使用しないほどの理由ではなく、同意していることです(ただし、それはかなり良い理由です!)。答えは主に、関係するコードの領域のみに変数のアクセスを制限する使用法を説明することであったと思われます。

ただし、データベースは、いわば「グローバルに」アクセスされることを目的として設計されているため、別の問題です。

例えば:

  • 通常、データベースには、データベースにアクセスする言語よりも先のタイプと構造の検証が組み込まれています
  • データベースは、トランザクションに基づいてほぼ満場一致で更新します。これにより、一貫性のない状態が防止されます。
  • データベース構造は、少なくともテーブルまたはオブジェクト構造に基づいて暗黙的に文書化されます。

最も重要なことは、データベースがグローバル変数とは異なる目的を果たすことです。データベースは、グローバル変数が特定のニッチを提供する大量の組織化されたデータを格納および検索するためのものです(正当な場合)。


1
ほら 私がほぼ同じ答えを書いている途中で、あなたはそれに私をbeatりました。:)
ジュール

@Julesの答えは、アプリケーション側の詳細を提供します。保管してください。
ジェフリースウィーニー

ただし、データアクセスをストアドプロシージャに完全に依存しない限り、その構造はすべて、意図したとおりにテーブルが使用されることを強制することに失敗します。または、その操作は適切な順序で実行されます。または、必要に応じてロック(トランザクション)が作成されます。
svidgen

こんにちは、Javaのような静的型付け言語を使用している場合、ポイント1と3はまだ適用可能ですか?
ジェスビンホセ

@aitchnyu必ずしもではありません。指摘されているのは、グローバル変数は通常そうではありませんが、データベースはデータを確実に共有するために構築されるということです。厳密な言語で自己文書化インターフェースを実装するオブジェクトは、ルーズタイプのNoSQLデータベースとは異なる目的を果たします。
ジェフリースウィーニー

10

しかし、それを見たとき、私はそれが本当に弱い説明だと思わざるを得ません。それはデータベースに保存されたデータを扱うこととどう違うのですか?

または、対話型デバイス、ファイル、共有メモリなどでの作業とは異なります。実行するたびにまったく同じことを行うプログラムは、非常に退屈でかなり役に立たないプログラムです。はい、それは弱い議論です。

私にとって、グローバル変数に関して違いを生む違いは、それらが隠れた保護されていない通信回線を形成することです。キーボードからの読み取りは非常に明白で保護されています。特定の関数呼び出しを行う必要があり、キーボードドライバーにアクセスできません。同じことがファイルアクセス、共有メモリ、および例のデータベースにも当てはまります。この関数がキーボードから読み取ること、その関数がファイルにアクセスすること、他の関数が共有メモリにアクセスすること(そして、その周りに保護があった方がよい)、さらに他の関数がデータベースにアクセスすることは、コードの読者には明らかです。

一方、グローバル変数では、まったく明らかではありません。APIはを呼び出すように指示しますfoo(this_argument, that_argument)。呼び出しシーケンスには、グローバル変数g_DangerWillRobinsonを何らかの値に設定する必要があると言うものはありませんが、呼び出す前foo(または呼び出し後に検査foo)です。


GoogleはC ++での非定数参照引数の使用を禁止しました。これは主に、定数でない参照を引数として使用するため、foo(x)変更さxれるコードの読者には明らかではないためですfoo。(関数定義と呼び出しサイトの両方がrefキーワードで参照パラメーターを修飾する必要があることを規定しているC#と比較してください。)この点についてはGoogleの標準に同意しませんが、その点は理解しています。

コードは1回作成され、数回修正されますが、それが良ければ、何度も何度も読み取られます。隠された通信回線は非常に悪いカルマです。C ++の非const参照は、通信の小さな隠れた線を表しています。優れたAPIまたは優れたIDEによって、「ああ!これは参照による呼び出しです」と表示されます。グローバル変数は、コミュニケーションの大きな隠れたラインです。


あなたの答えはより理にかなっています。
Billal Begueradj

8

引用された説明は、論法がばかげている点まで問題を単純化しすぎていると思います。もちろん、外部データベースの状態はグローバルな状態に寄与します。重要な問題はありますプログラムは(可変)グローバル状態に依存します。空白で文字列を分割するライブラリ関数がデータベースに保存された中間結果に依存する場合、少なくとも同じ目的で使用されるグローバル文字配列に反対するのと同じくらい、この設計に反対します。一方、この時点でビジネスデータを保存するために本格的なDBMSをアプリケーションに必要とせず、グローバルなメモリ内のキーと値の構造が必要であると判断した場合、これは必ずしも設計が悪いことの兆候ではありません。重要なのは、データを保存するためにどのソリューションを選択したとしても、この選択はシステムのごく一部に限定されるため、ほとんどのコンポーネントは展開用に選択されたソリューションに依存せず、単体および展開で単体テストされますソリューションは、後ほど変更することなく簡単に変更できます。


8

主に組み込みファームウェアを扱うソフトウェアエンジニアとして、私はほとんどの場合、モジュール間を移動するためにグローバル変数を使用しています。実際、組み込みのベストプラクティスです。これらは静的に割り当てられるため、ヒープ/スタックを爆破するリスクがなく、関数の開始/終了時にスタックの割り当て/クリーンアップに余分な時間がかかりません。

この欠点は、これらの変数がどのように使用されるかを考慮なければならないことであり、その多くはデータベースの論争に関連する同じ種類の考えに帰着します。変数の非同期読み取り/書き込みはアトミックでなければなりません。複数の場所で変数を書き込むことができる場合は、常に有効なデータを書き込むことを確認する必要があります。そのため、以前の書き込みはarbitrarily意的に置き換えられません(または、arbitrary意的な置き換えは安全なことです)。同じ変数が複数回読み取られる場合、変数が読み取りと読み取りの間で値を変更した場合、または変数のコピーを開始時に取得して一貫性のある値を使用して処理が行われる場合に何が起こるかを考慮する必要がありますその値は処理中に古くなります。

(最後の1つについては、非常に安全性に関連する航空機対策システムの契約の最初の日に、ソフトウェアチームは1週間ほど把握しようとしていたバグレポートを見ていました。開発ツールとコードのコピーをダウンロードするのに十分な時間を持っていたので、「その変数は読み取りと更新の間に更新されないか」と尋ねましたが、実際には答えが得られませんでした。結局のところ、彼らはまだ議論している間に、変数をアトミックに読み取るための保護コードを追加し、ローカルビルドを行い、基本的に「やあ、これを試して」と言いました。 。:)

したがって、グローバル変数は明白に悪いことではありませんが、それらについて慎重に考えなければ、さまざまな問題に開かれたままになります。


7

判断する側面に応じて、グローバル変数とデータベースアクセスは別々の世界になりますが、それらを依存関係として判断する限り、それらは同じです。

関数型プログラミングの純粋関数の定義を考えてみましょう。それは、入力として受け取るパラメーターのみに依存し、決定論的な出力を生成する必要があると述べています。つまり、同じ引数のセットを2回指定すると、同じ結果を生成する必要があります。

関数がグローバル変数に依存する場合、同じセットまたは引数に対して、呼び出し間でグローバル変数の値が変更されている可能性があるため、異なる出力を生成する可能性があるため、関数は純粋とは見なされなくなります。

ただし、グローバル変数を他の引数と同じように関数のインターフェイスの一部と見なすと、関数は決定論的とみなされる可能性があるため、問題ではありません。問題は、これは一見明らかな関数からの予期しない動作に驚かされるまで隠されているだけで、それから実装を読んで隠された依存関係を発見することです。

この部分、グローバル変数が隠れた依存関係になる瞬間は、私たちプログラマーによって悪と見なされるものです。コードの推論、動作の予測、再利用、テストが難しくなり、特に問題が発生した場合のデバッグと修正時間が増加します。

データベースへの依存関係を非表示にしても同じことが起こります。関数またはオブジェクトがデータベースクエリとコマンドを直接呼び出して、これらの依存関係を隠し、グローバル変数が引き起こすまったく同じトラブルを引き起こすことができます。または、明示的にすることもできますが、これは、リポジトリパターン、データストア、ゲートウェイなど、多くの名前で使用されるベストプラクティスと見なされます。

PS:並行性が関係するかどうかなど、この比較に重要な他の側面がありますが、その点については他の回答で説明します。


私はあなたが依存関係の角度からこれを取ったことが好きです。
cbojar

6

さて、歴史的なポイントから始めましょう。

アセンブリとCの典型的な組み合わせで書かれた古いアプリケーションを使用しています。関数はなく、プロシージャだけです。プロシージャから引数または戻り値を渡す場合は、グローバル変数を使用します。言うまでもなく、これを追跡するのは非常に難しく、一般に、すべてのプロシージャはすべてのグローバル変数で必要なことを実行できます。当然、人々は実行可能になるとすぐに別の方法で引数を渡し、値を返すことになりました(そうしないことがパフォーマンスに重大でない限り-たとえば、ビルドエンジン(Duke 3D)ソースコードを見る)。ここでグローバル変数の嫌悪が生まれました-各プロシージャがどのグローバル状態の一部を読み取って変更するかはほとんどわからず、プロシージャ呼び出しを安全にネストすることはできませんでした。

これは、グローバル変数の憎悪が過去のものであることを意味しますか?そうでもない。

まず、私が現在取り組んでいるプロジェクトで引数を渡すためのまったく同じアプローチを見たことに言及する必要があります。約10年前のプロジェクトで、C#で2つの参照型インスタンスを渡す場合。文字通りこのようなことをする正当な理由はなく、ほとんどの場合、貨物の栽培、またはC#の仕組みの完全な誤解から生まれたものです。

大きなポイントは、グローバル変数を追加することにより、そのグローバル変数にアクセスできるすべてのコードのスコープを拡大しているということです。「メソッドを短くする」などの推奨事項をすべて覚えていますか?600個のグローバル変数がある場合(再び、実世界の例:/)、すべてのメソッドスコープはそれらの600個のグローバル変数によって暗黙的に拡張され、誰が何にアクセスできるかを追跡する簡単な方法はありません。

間違って行われた場合(通常の方法:))、グローバル変数は相互に結合している可能性があります。しかし、それらがどのように結合されているかはわかりません。また、グローバル状態が常に一貫していることを保証するメカニズムはありません。物事の一貫性を保つために重要なセクションを導入しても、適切なACIDデータベースとの比較が不十分であることがわかります。

  • 「トランザクション」の前に古い値を保持しない限り、部分的な更新をロールバックする方法はありません。言うまでもなく、この時点で、値を引数として渡すことはすでに成功しています:)
  • 同じ状態にアクセスするすべての人は、同じ同期プロセスに従う必要があります。しかし、これを強制する方法はありません-クリティカルセクションのセットアップを忘れると、混乱します。
  • すべてのアクセスを正しく同期した場合でも、部分的に変更された状態にアクセスするネストされた呼び出しが存在する場合があります。これは、デッドロック(クリティカルセクションが再入可能でない場合)、または矛盾するデータ(再入可能である場合)に対処することを意味します。

これらの問題を解決することは可能ですか?あんまり。これを処理するにはカプセル化が必要であり、本当に厳しい規律が必要です。正しいことをするのは難しく、それは一般にソフトウェア開発の成功のための非常に良いレシピではありません:)

スコープが小さいと、コードの推論が容易になります。グローバル変数を使用すると、最も単純なコードでさえも、膨大な範囲のスコープが含まれます。

もちろん、これはグローバルスコープが悪であることを意味するものではありません。これは、最初の解決策ではありません。「実装が簡単で、保守が難しい」という典型的な例です。


物理的な世界によく似ています。物事をロールバックするのは非常に困難です。

これは良い答えですが、最初に論文のステートメント(TL; DRセクション)を立てることができます。
jpmc26

6

グローバル変数はツールであり、善と悪のために使用できます。

データベースはツールであり、善と悪のために使用できます。

元のポスターが指摘しているように、違いはそれほど大きくありません。

経験の浅い学生は、バグは他の人に起こるものだとしばしば考える。教師は、悪い設計にペナルティを科す単純化された理由として「グローバル変数は悪」を使用します。学生は一般に、100行のプログラムにバグがないからといって、同じ方法を10000行のプログラムに使用できるとは限らないことを理解していません。

データベースで作業する場合、プログラムがすべてであるため、グローバル状態を禁止することはできません。代わりに、ACIDやNormal Formsなどの詳細なガイドラインが表示されます。

人々がグローバル変数にACIDアプローチを使用していれば、それほど悪くはないでしょう。

一方、データベースの設計が適切でないと、悪夢になりかねません。


3
stackoverflowの典型的な学生の主張:助けて!私のコードは完璧ですが、正しく機能していません!
デビッドハンメン

「グローバル変数へのACIDアプローチ」-Clojureの参照を参照してください。
チャールズダフィー

@DavidHammenとあなたは、専門家は学生と違って頭脳を持っていると思いますか?
ビラルBegueradj

@BillalBEGUERADJ-それがプロと学生の違いです。長年の経験と、コードレビュー、テストなどの最善の努力にもかかわらず、私たちのコードは完璧ではないことを知っています。
デビッドハンメン


5

私にとっての主な悪事は、グローバルには並行性の問題に対する保護がないことです。Globalsでこのような問題を処理するメカニズムを追加できますが、並行性の問題を解決するほど、Globalsがデータベースを模倣し始めることがわかります。二次的な悪は、使用に関する契約ではありません。


3
例えば、errnoCの
デヴィッドHammen

1
これは、グローバルとデータベースが同じではない理由を正確に説明しています。他の違いもあるかもしれませんが、あなたの特定の投稿はコンセプトを完全に破壊します。あなたが簡単なコード例を与えたなら、私はあなたが多くの賛成票を得ると確信しています。例:MyFunc(){x = globalVar * 5; // ....その他の処理; y = globalVar * 34; // Ooops、他のスレッドは他の処理中にglobalVarを変更した可能性があり、xとyは計算でglobalVarに異なる値を使用しているため、望ましい結果はほとんど確実に得られません。
ダンク

5

他の回答のいくつかは、データベースを使用するのが良い理由を説明しようとします。彼らは間違ってる!データベースはグローバルな状態であるため、シングルトンやグローバル変数と同じくらい悪です。代わりにローカルのMapまたはArrayを簡単に使用できる場合に、データベースを使用するのはあらゆる種類の誤りです。

グローバル変数により、グローバルアクセスが可能になり、不正使用のリスクが伴います。グローバル変数にも利点があります。グローバル変数は一般に、決して使用すべきでないものではなく、避けるべきものであると言われています。簡単に回避できる場合は、回避する必要があります。しかし、利点が欠点を上回る場合は、もちろんそれらを使用する必要があります!*

グローバル変数と同じように、グローバル状態であるデータベースにもまったく同じことが適用されます**。データベースにアクセスせずに実行でき、結果のロジックが必要なすべてを実行し、同様に複雑な場合、データベースを使用するとプロジェクトにリスクが増加しますが、対応するメリットはありません。

現実には、多くのアプリケーションは設計によってグローバルな状態を必要とし、時には永続的なグローバルな状態を必要とします。それがファイル、データベースなどを持っている理由です。


*ここでの例外は学生です。学生がグローバル変数を使用できないようにすることは理にかなっています。

**一部の回答では、データベースは他の形式のグローバルステートよりも何らかの方法で保護されていると誤って主張しています(グローバル変数だけでなく、グローバルステートに関する質問です)。それはブロックです。データベースシナリオで提供される主な保護は慣例によるもので、他のグローバルステートでもまったく同じです。また、ほとんどの言語constでは、コンストラクターで設定された後に状態を変更できないクラス、またはスレッド情報やプログラムの状態を考慮に入れることができるゲッターとセッターの形式で、グローバル状態の多くの追加保護を許可しています。


2

ある意味では、グローバル変数とデータベースの区別は、オブジェクトのプライベートメンバーとパブリックメンバーの区別に似ています(だれでもまだパブリックフィールドを使用していると仮定)。プログラム全体をオブジェクトと考えると、グローバルはプライベート変数であり、データベースはパブリックフィールドです。

ここでの重要な区別は、想定される責任の1つです。

オブジェクトを記述するとき、メンバーメソッドを管理する人はだれでも、プライベートフィールドが適切に動作することを保証すると想定されています。ただし、パブリックフィールドの状態に関する仮定は既に放棄し、細心の注意を払って処理します。

グローバルv / sデータベースにも、より広いレベルで同じ仮定が適用されます。また、プログラミング言語/エコシステムは、(非共有メモリ)グローバルv / sデータベースに適用するのと同じように、プライベートv / s publicのアクセス制限を保証します。

マルチスレッドが登場すると、プライベートv / sパブリックv / sグローバルv / sデータベースの概念は、スペクトルに沿った単なる区別になります。

static int global; // within process memory space
static int dbvar; // mirrors/caches data outside process memory space

class Cls {
    public: static int class_public; // essentially the same as global
    private: static int class_private; // but public to all methods in class

    private: static void method() {
        static int method_private; // but public to all scopes in method
        // ...
        {
            static int scope1_private; // mutex guarded
            int the_only_truly_private_data;
        }
        // ...
        {
            static int scope2_private; // mutex guarded
        }
    }
}

1

データベースグローバルな状態にすることできますが、常にそうである必要はありません。あなたがコントロールできないという仮定には同意しません。それを管理する1つの方法は、ロックとセキュリティです。これは、レコード、テーブル、またはデータベース全体で実行できます。別のアプローチは、データが古い場合にレコードの変更を防止する何らかの種類のバージョンフィールドを持つことです。

グローバル変数と同様に、データベースの値はロックを解除すると変更できますが、アクセスを制御する方法は多数あります(すべての開発者に、データの変更を許可されたアカウントへのパスワードを与えないでください)。アクセスが制限されている変数がある場合、それはあまりグローバルではありません。


0

いくつかの違いがあります。

  • データベースの値その場で変更できます。一方、コードに設定されているグローバルの値は、アプリケーションを再デプロイしてコードを変更しない限り変更できません。実際、これは意図的なものです。データベースは、時間の経過とともに変化する可能性のある値を対象としていますが、グローバル変数は、決して変化ないもの、および実際のデータが含まれていない場合にのみ使用する必要があります。

  • データベース値(行、列)には、データベース内のコンテキストとリレーショナルマッピングがあります。この関係は、Jailerなどのツールを使用して簡単に抽出および分析できます(たとえば)。一方、グローバル変数はわずかに異なります。すべての使用法を見つけることができますが、変数が他の世界と相互作用するすべての方法を私に伝えることは不可能でしょう。

  • グローバル変数は高速です。データベースから何かを取得するには、データベース接続を確立し、selectを実行してから、データベース接続を閉じる必要があります。その上に必要になる可能性のある型変換があります。コードでアクセスされているグローバルと比較してください。

これらは私が今考えることができる唯一のものですが、私はもっとあると確信しています。簡単に言えば、これらは2つの異なるものであり、異なる目的に使用されるべきです。


0

もちろん、グローバルは必ずしも不適切ではありません。それらは正当な用途があるために存在します。グローバルの主な問題、およびそれらを回避するための警告の主なソースは、グローバルを使用するコードがその唯一のグローバルに接続されることです。

たとえば、サーバー名を格納するHTTPサーバーを考えます。

サーバー名をグローバルに保存する場合、プロセスは2つの異なるサーバー名のロジックを同時に実行できません。おそらく、元の設計では一度に複数のサーバーインスタンスを実行することを想定していなかったかもしれませんが、後でそれを実行することにした場合、サーバー名がグローバルにある場合は単純にできません。

対照的に、サーバー名がデータベースにある場合、問題はありません。HTTPサーバーのインスタンスごとに、そのデータベースのインスタンスを1つ作成するだけです。サーバーの各インスタンスにはデータベースの独自のインスタンスがあるため、独自のサーバー名を持つことができます。

したがって、グローバルに対する主な異論は、そのグローバルにアクセスするすべてのコードに対して1つの値のみであり、データベースエントリには適用されないということです。同じコードは、特定のエントリに対して異なる値を持つ個別のデータベースインスタンスに簡単にアクセスできます。


0

これは興味深い質問だと思いますが、「グローバル状態」という用語の下で混同されている2つの主要な問題があるため、答えるのは少し難しいです。1つ目は「グローバルカップリング」の概念です。その証拠は、グローバル状態に与えられた代替が依存性注入であることです。問題は、DIが必ずしもグローバルな状態を排除するわけではないということです。つまり、グローバル状態に依存関係を注入することは絶対に可能であり、一般的です。DIが行うことは、グローバル変数と一般的に使用されるシングルトンパターンに伴うカップリングを削除することです。わずかにわかりにくいデザインを除けば、この種のカップリングを削除することのマイナス面はほとんどなく、カップリングを削除する利点は、これらのグローバルへの依存関係の数とともに指数関数的に増加します。

この他の側面は共有状態です。グローバルに共有された状態と一般に共有された状態の間に本当に明確な区別があるかどうかはわかりませんが、コストとメリットははるかに微妙です。簡単に言えば、共有状態を有効にする必要のあるソフトウェアシステムは無数にあります。たとえば、ビットコインは、分散方式でグローバルに(文字通り)状態を共有する非常に賢い方法です。巨大なボトルネックを作成せずに可変状態を適切に共有することは困難ですが、便利です。したがって、実際にそれを行う必要がない場合は、共有された可変状態を最小化することにより、アプリケーションを簡素化できます。

したがって、データベースがグローバルとどのように異なるかという問題も、これらの2つの側面に分かれています。カップリングを導入していますか?はい、できますが、アプリケーションの設計方法とデータベースの設計方法に大きく依存します。データベースが設計の詳細なしでグローバル結合を導入するかどうかについて単一の答えを得るにはあまりにも多くの要因があります。状態の共有を導入するかどうかについては、まあ、それは一種のデータベースの主要なポイントです。問題は、彼らがうまくやっているかどうかです。繰り返しますが、これは複雑すぎて、選択肢や他の多くのトレードオフなど、他の多くの情報なしでは答えることができないと思います。


0

「グローバル変数」のような振る舞いは、データベース管理者(DBA)によって支払われる代償です。なぜなら、それは彼らの仕事をするのに必要な悪だからです。

グローバル変数の問題は、他のいくつかが指摘しているように、arbitrary意的な問題ではありません。問題は、変数を使用すると、誰がどのような方法で変数を使用しているかを判断することが難しくなるため、プログラムの動作がますます予測不能になることです。これは現代のソフトウェアにとって大きな問題です。なぜなら、現代のソフトウェアは通常、多くの柔軟なことを行うよう求められるからです。実行中に、数十億または数兆の複雑な状態操作を行う場合があります。そのソフトウェアがこれらの数十億または数兆のオペレーションで何をするかについての真の声明を証明する能力は非常に貴重です。

最新のソフトウェアの場合、カプセル化など、すべての言語がこれを支援するツールを提供します。それを使用しないという選択は不必要であり、「グローバルは悪」という考え方につながります。ソフトウェア開発分野の多くの地域では、それらを使用する唯一の人々は、より良いコーディング方法を知らない人々です。これは、彼らが直接問題を抱えているだけでなく、開発者が自分が何をしていたのか知​​らなかったことを間接的に示唆していることを意味します。他の地域では、グローバルが完全に正常であることがわかります(特に組み込みソフトウェアは、グローバルが大好きです。一部はISRとうまく機能するためです)。しかし、多くのソフトウェア開発者の中で彼らは少数派の声であるため、あなたが聞く唯一の声は「グローバルは悪」です。

データベース開発は、これらの少数の音声状況の1つです。DBAの作業に必要なツールは非常に強力であり、その理論はカプセル化に根ざしていません。データベースのパフォーマンスを少しずつ引き出すには、グローバルと同様に、すべてに自由にアクセスできる必要があります。巨大な1億行(またはそれ以上!)のデータベースの1つを使用すると、DBエンジンがパンチを保持できない理由を理解できます。

彼らはそのための代価、親愛なる代価を支払います。DBAはツールがそれらを保護しないため、細部に注意を払ってほとんど病理学的であるように強制されます。保護の方法で最も優れているのは、ACIDまたは外部キーです。病理学的ではないものは、完全に使用不能であるか、破損している完全な混乱のテーブルで自分自身を見つけます。

10万行のソフトウェアパッケージを用意することは珍しくありません。理論的には、ソフトウェアのどの行も、いつでもグローバルに影響を与える可能性があります。DBAでは、データベースを変更できる10万の異なるクエリを見つけることはありません。自分から身を守るために必要な細部に注意を払って維持するのは不合理です。DBAにそのような大きなものがある場合、アクセサーを使用してデータベースを意図的にカプセル化し、「グローバルな」問題を回避し、「安全な」メカニズムを通じて可能な限り多くの作業を行います。このように、プッシュが突き進むと、データベースの人々でさえグローバルを避けます。それらは単に多くの危険を伴うだけであり、同じくらい強いが、それほど危険ではない代替があります。

他の条件がすべて同じである場合、壊れたガラスの上、またはきれいに掃除された歩道の上を歩き回りますか?はい、割れたガラスの上を歩くことができます。はい、それをやって生計を立てている人さえいます。しかし、それでも、彼らが歩道を掃除して先に進むようにしましょう!


0

前提は間違っていると思います。データベースが(非常に大きな)コンテキストオブジェクトではなく「グローバル状態」である必要がある理由はありません。コードがグローバル変数または固定グローバルデータベース接続パラメーターを介して使用している特定のデータベースにバインドしている場合、他のグローバル状態と違いはありません。一方、データベース接続のコンテキストオブジェクトを適切にやり取りする場合、それはグローバルな状態ではなく、大きな(および広く使用されている)コンテキスト状態です。

違いの測定は簡単です。コードに侵襲的な変更を加えることなく、それぞれ独自のデータベースを使用して、プログラムロジックの2つのインスタンスを単一のプログラム/プロセスで実行できますか?その場合、データベースは実際には「グローバル状態」ではありません。


-2

グローバルは悪ではありません。それらは単なるツールです。グローバルの誤用は、他のプログラミング機能の誤用と同様に問題があります。

私の一般的な推奨事項は、他のソリューションが最適ではない、十分に理解され、考え抜かれた状況でのみグローバルを使用することです。最も重要なことは、グローバル値が変更される可能性がある場所を十分に文書化し、マルチスレッドを実行している場合は、グローバルおよび共依存グローバルがトランザクションの方法でアクセスされるようにすることです。


ダウンボーターの何人かはあなたのダウンボートを説明してもらえますか?説明なしに下票するのは失礼なようです。
バイロンジョーンズ

-2

読み取り専用パターン。印刷時にデータが最新ではないと仮定します。キューは、別の方法で競合を書き込みまたは処理します。地獄の悪魔にようこそ、あなたはグローバルデータベースを使用しています。

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