このフォーラムで、グローバル変数を使用することは大罪であり、シングルトンを実装することは犯罪であると何度も耳にしました。
古い良い定数は、これらの不名誉な慣行のすべての機能を備えていることが頭に浮かびました。それらはグローバルにアクセスされ、これまでで最もグローバルな状態を導入することは間違いありません。
問題は、定数に対してもジハードを宣言し、代わりに DI、IoC、またはその他のスタイリッシュな言葉などの最新のものをすべて使用するべきではないかということです。
このフォーラムで、グローバル変数を使用することは大罪であり、シングルトンを実装することは犯罪であると何度も耳にしました。
古い良い定数は、これらの不名誉な慣行のすべての機能を備えていることが頭に浮かびました。それらはグローバルにアクセスされ、これまでで最もグローバルな状態を導入することは間違いありません。
問題は、定数に対してもジハードを宣言し、代わりに DI、IoC、またはその他のスタイリッシュな言葉などの最新のものをすべて使用するべきではないかということです。
一般的に言えば、定数は避けてください。それらは、消費者からグローバルスコープへの結合を導入します。つまり、消費者は外部のものに依存しています。これは自明ではありません。
class Foo
{
public function doSomething()
{
if (ENV === ENV_DEV) {
// do something this way
} else {
// do something that way
}
}
}
の内部構造を知らなければ、doSomething
その定数を持つグローバル スコープに依存関係があることはわかりません。そのため、コードがやや理解しにくくなるだけでなく、再利用方法も制限されます。
上記は、値が 1 つしかない定数にも当てはまります。
public function log($message)
{
fwrite(LOGFILE, $message);
}
ここで、定数は、外部のどこかで次のように定義されたファイル リソースを指します。
define('LOGFILE', fopen('/path/to/logfile'));
そして、これは を使用するのと同じくらい自明ではありませんENV
。これは、クラス外に何かが存在することを必要とする依存関係です。そして、そのオブジェクトを扱うためには、それを知らなければなりません。この定数を使用するクラスはこの詳細を隠しているため、定数が存在することを確認せずに何かをログに記録しようとすると、なぜ機能しないのか疑問に思うかもしれません。リソースである必要はなく、LOGFILE
単純にパスを文字列として含めることができます。同じ結果です。
コンシューマーのグローバル定数に依存するには、単体テストでグローバル状態をセットアップする必要もあります。単体テストのポイントは単体で単体をテストすることであり、環境を特定の状態に置かなければならないことがこれを妨げるため、定数が固定値であっても、これは一般的に避けたいことです。
さらに、グローバル定数を使用すると、異なるライブラリの定数が衝突するという脅威が常に発生します。経験則として、グローバル スコープには何も入れないでください。定数を使用する必要がある場合は、名前空間を使用して定数をクラスター化します。
ただし、名前空間付きの定数には結合に関して同じ問題があり、クラス定数にも同じ問題があることに注意してください。この結合が同じ名前空間内にある限り、それほど重要ではありませんが、さまざまな名前空間の定数に結合し始めると、再び再利用が妨げられます。さらに言えば、定数パブリック API を検討してください。
定数を使用する代わりに、不変の値オブジェクトを使用することもできます。たとえば、次のようになります。
class Environment
{
private $value;
public function __construct($value)
{
$this->assertValueIsAllowedValue($value);
$this->value = $value;
}
public function getValue() {
// …
このようにして、値が有効であることを確認するだけでなく、これらの値を必要とするオブジェクトに渡すことができます。いつものように、YMMV。これは単なるオプションです。単一の定数によってコードが使用不能になることはありませんが、定数に大きく依存すると悪影響が生じるため、経験則として定数を最小限に抑えるようにしてください。
関連する補足として、次のことにも興味があるかもしれません。
グローバル変数が悪い習慣と見なされる主な理由は、システムの一部で変更され、別の部分で使用される可能性があり、これら 2 つのコードの間に直接のリンクがないためです。
これは潜在的なバグにつながります。グローバル変数が使用される場所や変更方法をすべて知らずに (または考慮せずに) グローバル変数を使用するコードを作成する可能性があるからです。またはその逆で、変更がコードの他の無関係な部分に与える可能性がある影響を認識せずに、グローバルに変更を加えるコードを記述します。
定数は、定数であるため、この問題を共有しません。それらは一度定義されると変更できないため、上記の段落で説明した発行は発生しません。
したがって、グローバルに使用しても問題ありません。
そうは言っても、define
定数の作成に使用するが、さまざまな状況で定数を異なる方法で宣言する、不十分に作成された PHP コードを見たことがあります。これは定数の誤用です。定数は絶対に固定された値でなければなりません。単一の値のみにする必要があります。プログラムの実行ごとに異なる値になる可能性があるものがある場合は、定数として定義しないでください。そのようなものは実際に変数である必要があり、他の変数と同じ規則に従う必要があります。
この種の誤用は、PHP のようなスクリプト言語でのみ発生します。定数は 1 か所で固定値に対して 1 回しか定義できないため、コンパイル済み言語では発生しません。
グローバル変数とグローバル定数には大きな違いがあります。
グローバル変数が敬遠される主な理由は、いつでも変更できるからです。呼び出し/実行順序にあらゆる種類の隠れた依存関係を導入する可能性があり、グローバルが変更されたかどうかとその方法に応じて、同じコードが機能することもあれば、他のコードが機能しないこともあります。同時実行または並列処理を扱っている場合は、明らかに悪いモジョがさらに増加する可能性があります。
グローバル定数は、常にコード全体でまったく同じです (またはそうあるべきです)。コードの実行が開始されると、それを表示するすべてのコードが毎回同じものを見ることが保証されます。これは、偶発的な依存関係を導入する危険がないことを意味します。定数を使用すると、コードを変更する必要がある場合に複数の場所で値を更新する必要がないため、実際には信頼性を向上させるのに非常に役立ちます。(ヒューマンエラーを決して過小評価しないでください!)
シングルトンはまったく別の問題です。これは、基本的にグローバル変数のオブジェクト指向バージョンになる可能性がある、よく悪用される設計パターンです。一部の言語 (C++ など) では、初期化の順序に注意しないと、非常にうまくいかないこともあります。ただし、通常はより優れた代替手段がありますが、場合によっては便利なパターンになることもあります (ただし、多少の作業が必要になる場合もあります)。
編集:簡単に説明すると、質問の中で、グローバル定数が「これまでで最もグローバルな状態」を導入すると述べました。ソース コードが修正されるのと同じ方法でグローバル定数が修正される (または修正される必要がある) ため、これはあまり正確ではありません。プログラムの静的な性質を定義しますが、「状態」は通常、動的な実行時の概念 (つまり、変更可能なもの) として理解されます。