タイトルがわかりにくいかもしれないので、例を挙げます。
abstract Class A {
public static $property = null;
}
Class B extends A {
}
Class C extends A {
}
A を拡張するすべてのクラスで "None" メソッドを使用したいのですが、このメソッドは呼び出されたクラスのインスタンス (常に同じ) を返す必要があります。そう:
B::None() // Returns a "default" B
C::None() // Returns a "default" C
これはなぜですか: いくつかの種類のアクティビティに割り当てられている場合と割り当てられていない場合がある (簡略化して) いくつかのスロットがあります。したがって、サーフィン用のスロットと水泳用のスロットを持つことができます。そのスロットは null の場合があります。私のコードでは、報告するときに、もちろん次のようなことができます
if (Slot.Surfing == null) {
println "Not surfing anywhere";
} else {
println Slot.Surfing.Location;
}
しかし、私はまったくチェックせずに書きたいと思います
println Slot.Surfing.Location;
スロットを Surfing::None() に事前に割り当てます。実際には、スーパークラスでそれを行い、None の「適切な」インスタンスを自動的に割り当てるようにします。
このように、Slot.Surfing(nowhere) は Slot.Swimming(nowhere) とは異なる nullですが、私にとってそれは実際にはfeatureになります。
問題は、私がどこかで泳いでいることを本当に確認したい場合、
if (Slot.Surfing == Surfing::None()) {
動作します。そのために、None() は常に同じオブジェクトを返す必要があります。サーフィンのフィールド、おそらく非 i18n の整数値でチェックを実行できます... 0 または -1 が典型的な選択です... しかし、その目的のためにプロパティを追加するのは適切に設計されていないようです。
注: これはシングルトン (アンチ) パターンと多くの類似点がありますが、実際にはシングルトンではありません (下部を参照)。
ただし、メソッドを Aに実装する場合None()
、静的プロパティは、各子クラスではなく、A で 1 回だけ「有効」になるという事実を処理する必要があります。したがって、B のデフォルト インスタンスを作成し、それを A に保存すると、その後の Aの他のサブクラスへのすべての呼び出しで、その 1 つのインスタンスが検索されて返されC::None()
、B のインスタンスになります。
None() メソッドに毎回新しいデフォルト インスタンスを作成させることができます。これは機能しますが、現在、いくつかの「デフォルト」の B があり、状況によっては、両方に設定されている 2 つのプロパティが、まったく正しく異なるB::None()
と見なされます。
最後に、この回避策を思いつきました(PHP 5.3.+):
private static $nones = array(); // array of already created "null" instances
protected static function None() {
// If I do not already have an instance for this class...
if (!isset(self::$nones[$ChildName = get_called_class()])) {
// ... I create a default instance.
self::$nones[$ChildName] = new $ChildName(/*...*/);
}
// And I return the default instance.
return self::$nones[$myClass];
}
Stack Overflow などでいくつかの質問と回答を確認しましたが、最も関連性の高いものは、同じアプローチに相当するものを採用しています ($instance
呼び出されたクラスの名前にインデックスが付けられた配列に注意してください)。
class A {
public static function getInstance(){
// Maybe use this function to implement the singleton pattern ...
return self::$instance[get_called_class()];
}
public function className(){
return get_class(self::getInstance());
}
}
しかし、私はまだ耳の後ろが濡れているためか、私にはこのアプローチは臭いです。親で子の静的プロパティを宣言し、スーパークラスからアクセスする方法が必要だと思います(もちろん、それから私は自問する必要があります:Aが「下流の」静的プロパティを宣言する場合、Bはから継承しますA、および B から C の場合、そのプロパティはどこにあるのか、または現在どこにあるべきなのか? -- 満足のいく答えはありません)。
補遺 - シングルトン
上記のアプローチは、実際には a とそれほど違いはありませんSingleton
。依存性注入によってシングルトンを取り除くことができるようです(Touki さんに教えていただいてありがとうございます) 。しかし、この場合None_C
、たとえば、C のインスタンスへの参照にデフォルト値を必要とする可能性のあるすべてのメソッドに渡す必要があります。次にNone_C
、オブジェクトにプッシュし、Configuration
A のサブクラスを認識させる必要があります。私が宣言するかもしれないこと。一見すると、これはさらに臭いがします (ただし、実際には A の別のサブクラスを追加すると、システムの構成が変更されます...これがを変更する理由になりますConfiguration
)。
TL;DR
つまり、上記のアプローチが機能することを前提として、長い話を短くするために、
- 親クラスに、OOPの観点から受け入れられる「アクティブな」子の静的配列を維持させていますか?
- それを行うためのより良いおよび/またはよりクリーンな方法はありますか?