すべてに対して普遍的な遅延評価コンテナーを持つことはできないと思います。
まず、あなたが本当に持っているものについて話し合いましょう。怠惰な評価ではないと思います。遅延評価は、値が実際に必要とされるポイントまで評価を遅らせ、その値に対するさらなる要求で既に評価された値を共有することとして定義されます。
私の頭に浮かぶ典型的な例は、データベース接続です。必要なときにその接続を使用できるようにすべてを準備しますが、実際にデータベースクエリが必要な場合にのみ、接続が作成され、後続のクエリと共有されます。
典型的な実装では、接続文字列をコンストラクターに渡し、それを内部に格納します。クエリ メソッドが呼び出されると、最初に接続ハンドルを返すメソッドが呼び出され、そのハンドルが作成され、接続と共に保存されます。存在しない場合は文字列。そのオブジェクトを後で呼び出すと、既存の接続が再利用されます。
このようなデータベース オブジェクトは、データベース接続の遅延評価に適しています。本当に必要な場合にのみ作成され、他のすべてのクエリで共有されます。
あなたの実装を見ると、「本当に必要な場合にのみ評価する」という資格はなく、一度作成された値のみが保存されます。したがって、実際にはある種のキャッシュにすぎません。
また、高価な計算をグローバルに一度だけ評価するという問題を実際に解決することはできません。インスタンスが 2 つある場合は、高価な関数を 2 回実行します。しかし一方で、それを 2 回評価しないと、グローバルな状態が導入されます。これは、明示的に宣言されていない限り、悪いことと見なされるべきです。通常、コードを適切にテストすることが非常に難しくなります。個人的にはそれは避けたいです。
データ計算メソッドとは異なるオブジェクトに実際のデータメソッドを含めることは可能ですか?
Zend Framework がキャッシュ パターン ( ) をどのように提供しているか\Zend\Cache\Pattern\{Callback,Class,Object}Cache
を見ると、実際の作業クラスがデコレーターをラップして取得していることがわかります。保存された値を取得して読み戻すためのすべての内部処理は内部で処理され、外部からは以前と同じようにメソッドを呼び出します。
欠点は、元のクラスの型のオブジェクトがないことです。したがって、型ヒントを使用する場合、元のオブジェクトの代わりに装飾されたキャッシュ オブジェクトを渡すことはできません。解決策は、インターフェイスを実装することです。元のクラスは実際の関数を使用してそれを実装し、キャッシュ デコレーターを拡張してインターフェイスも実装する別のクラスを作成します。このオブジェクトはタイプ ヒンティング チェックに合格しますが、すべてのインターフェイス メソッドを手動で実装する必要があります。これらのメソッドは、そうでなければインターセプトされる内部マジック関数への呼び出しを渡すだけです。
interface Foo
{
public function foo();
}
class FooExpensive implements Foo
{
public function foo()
{
sleep(100);
return "bar";
}
}
class FooCached extends \Zend\Cache\Pattern\ObjectPattern implements Foo
{
public function foo()
{
//internally uses instance of FooExpensive to calculate once
$args = func_get_args();
return $this->call(__FUNCTION__, $args);
}
}
PHP では、少なくともこれら 2 つのクラスと 1 つのインターフェイスなしでキャッシュを実装することは不可能であることがわかりました (ただし、一方で、インターフェイスに対して実装することは良いことであり、気にする必要はありません)。ネイティブ キャッシュ オブジェクトをそのまま直接使用することはできません。
ネストされた配列を持つキャッシュを使用して、パラメーターを持つ計算メソッドを持つことは可能ですか?
パラメータは上記の実装で機能し、キャッシュ キーの内部生成に使用されます。おそらく、\Zend\Cache\Pattern\CallbackCache::generateCallbackKey
メソッドを確認する必要があります。
PHP では、メインの検索メソッドにマジック メソッド (__get() または __call()) を使用できます。クラス docblock の "@property" と組み合わせると、各 "仮想" プロパティの型ヒントが可能になります。
魔法の方法は悪です。ドキュメント ブロックは、実際に動作するコードではないため、時代遅れであると見なす必要があります。のように任意のプロパティに任意の値を格納できる、非常にわかりやすい値オブジェクト コードでマジック ゲッターとセッターを使用することは許容できると思いましたが、 .stdClass
__call
通常のメソッドと区別するために、「get_someValue()」のようなメソッド名をよく使用します。「someValue」は実際のキーです。
これは PSR-1 の違反であると考えます: 「4.3. メソッド: メソッド名は で宣言する必要がありcamelCase()
ます。」そして、これらのメソッドを特別なものとしてマークする理由はありますか? 彼らはまったく特別ですか?は値を返しますね。
データ計算を複数のオブジェクトに分散して、ある種の関心を分離することは可能ですか?
オブジェクトの複雑な構造をキャッシュする場合、これは完全に可能です。
一部の値を事前に初期化することは可能ですか?
これはキャッシュの問題ではなく、実装自体の問題です。高価な計算を実行するのではなく、プリセット値を返すことのポイントは何ですか? それが実際のユースケースである場合 (パラメーターが定義された範囲外にある場合に即座に NULL を返すなど)、実装自体の一部である必要があります。このような場合に値を返すために、オブジェクトの周囲に追加のレイヤーを当てにしないでください。
動的計画法から中間値を保存することは、これの正当な使用例ですか?
動的計画法の問題がありますか? リンクしたウィキペディアのページに次の文があります。
動的計画法を適用するには、問題が持つ必要がある 2 つの重要な属性があります。それは、最適部分構造と重複部分問題です。重複しない部分問題に最適解を組み合わせることで問題を解決できる場合、その戦略は代わりに「分割統治」と呼ばれます。
あなたの例の遅延評価部分を解決するように見える既存のパターンが既にあると思います:Singleton、ServiceLocator、Factory。(私はここでシングルトンを宣伝しているわけではありません!)
「プロミス」の概念もあります。要求された場合に後で実際の値を返すことを約束するオブジェクトが返されますが、値が現在必要でない限り、代わりに渡すことができる値の置換として機能します。このブログ投稿を読むことをお勧めします: http://blog.ircmaxell.com/2013/01/promise-for-clean-code.html
これを PHP で実装するためのベスト プラクティスは何ですか? 「詳細」の一部は悪くて醜いですか?
おそらくフィボナッチの例に近い例を使用しました。この例で気に入らない点は、単一のインスタンスを使用してすべての値を収集することです。ある意味では、ここでグローバルな状態を集約しています。これがおそらく、この概念全体の目的です。しかし、グローバルな状態は悪であり、私はその余分なレイヤーが好きではありません. そして、パラメータの問題を十分に解決していません。
bar()
insideへの呼び出しが実際に 2 つあるのはなぜだろうかfoo()
。より明白な方法は、結果を で直接複製しfoo()
、それを「追加」することです。
全体として、私は今まであまり感銘を受けていません。この単純なレベルで、このような汎用ソリューションの実際の使用例を予測することはできません。私は IDE の自動提案サポートが好きで、ダックタイピング (互換性をシミュレートするだけで、インスタンスを保証できないオブジェクトを渡す) は好きではありません。