私はいくつかのコードをリファクタリングしようとしているので、依存関係注入を使用しています。
次の(意味のない)サンプルクラスを見てください。
_class Foo {
protected $min;
protected $max;
public $bar;
public function __construct($min, $max) {
$this->min = $min;
$this->max = $max;
$this->bar = new Bar($this->calc());
}
public function calc() {
$number = Rand($this->min, $this->max);
return new Quz($number);
}
}
_
(コンテナーで)依存性注入を使用するには、これを次のように変更します。
_class Foo {
protected $barFactory;
protected $quzFactory;
protected $min;
protected $max;
public $bar;
public function __construct(BarFactoryInterface $barFactory, QuzFactoryInterface $quzFactory) {
$this->barFactory = $barFactory;
$this->quxFactory = $quxFactory;
}
public function init($min, $max) {
$this->min = $min;
$this->max = $max;
$this->bar = $this->barFactory->create($this->calc());
}
public function calc() {
$number = Rand($this->min, $this->max);
return $this->quxFactory->create($number);
}
}
_
init()
関数が必要ですか?各クラスのファクトリメソッドまたはクラスが本当に必要ですか?
「Bar」と「Quz」のインスタンスを作成しているので、それらを分離するためにファクトリーを使用することは理にかなっています。
ただし、コンストラクタでBar
を作成するだけなので、ファクトリを取得できず、代わりにBar
のインスタンスを取得します。そうすることは、ファクトリなしの依存性注入の例です。これにより、クライアントコードがオブジェクトを提供できるようになります。意図がある場合とそうでない場合は、ファクトリを使用します。実際には、Bar
を使用していないため、スキップできます。
編集:calc
の結果を使用してBar
を作成したいと思います。 Bar
インスタンスの代わりにファクトリを使用すると、Bar
がQuz
に対して有効な結果で作成され、min
とmax
が指定されていても、bar
がパブリックであり、クライアントコードがそれを上書きできるため、カプセル化が向上します。その保護に対抗します。
Quz
に従って、クライアントコードがcalc
を呼び出すたびに新しいインスタンスを作成するため、より多くのインスタンスを取得できるようにファクトリが必要になります。
コンストラクタを置き換えるために本当にinit()関数が必要ですか?
コンストラクターでファクトリーを取ることができます。別のメソッドを使用すると、開発者がコンストラクターを呼び出してinit
の呼び出しをスキップできるため、問題が発生します。
ここで、ファクトリを非表示にして、クライアントコードで同じファクトリを共有する複数のインスタンスを作成する場合は、そのためのファクトリを作成できます。私はそれをする必要がないと言わざるを得ません。
それとも私はそれをひどく間違っていますか?
init
をコンストラクターから分離します。クライアントコードがインスタンスを作成し、calc
を呼び出さずにinit
を呼び出すとどうなるかを考えてみましょう。コードでは、フィールドが初期化されなかったため(言語によって)、乱数は0(またはデフォルト値)またはゴミ箱になります。
最悪の場合、Bar
を実際に使用した場合、init
で初期化されているため初期化されていないため、無効なポインターになります。
オブジェクトまたはファクトリーを注入すると、さまざまな目的が解決され、さまざまな意図が伝達されます。
ファクトリを渡すと、新しいオブジェクトはそのファクトリから新しいオブジェクトを作成する場合と作成しない場合があります。これらのオブジェクトを事前に構成することはできません(少なくとも、ファクトリで静的プロパティを設定するようなコードの匂いがない限りは)。注入できるのは、工場があるものだけです。時々、これはまさにあなたが望むものであるかもしれません-しかし、あなたはたまたま工場である普通のオブジェクトを注入するのと同じようにそれを見ることができます。
「通常の」オブジェクトを挿入する場合、何でも渡すことができます-ファクトリのないオブジェクトは問題ありません。その目的のためだけにファクトリークラスを作成することは賢明ではありません。
なぜコードインジェクトをしたいのかと自問する場合、おそらくconfiguration/state/behavior/whatever-you-call-itを渡したいでしょう。受け継がれる工場はそれを非常に制限します。たとえば、構成できなかったデータベースコネクタをどのように渡すことができますか?また、再利用もできませんでした。
あなたはここでコンセプトを混同していると思います(つまり、ファクトリーパターンと依存性注入)-一緒に使用できますが、そうする必要はありません。
通常、次の2つの条件が満たされた場合にファクトリを使用することは理にかなっています。
これらの2つの基準が満たされていない場合、コンストラクターを介してインスタンスを渡すだけで十分に機能し、費用対効果が最も高くなります。
ファクトリー自体の基本原則は基準2であるため、それがない限り、ファクトリーを使用することも、DIを使用することも意味がありません。
オリジナルのFoo
は3つのことを行います:
$this->bar = new Bar(new Quz(Rand($min, $max))
calc()
をnew Quz(Rand($min, $max))
として定義します$min
および$max
したがって、calcはそれらを渡す必要はありませんオリジナルのFoo
はすでに依存性注入を行っています。それは主に建設です。 Rand
は動作のほんの一部であり、動作と構造を分離したいので、構造にハードコーディングされていることは少し奇妙ですが、私の主な不満ではありません。
私の主な不満は、これが慣れているのを見ていません。これは不自然な例のように読めます。私はこれのどれもが助けになっていることを本当に見ることができません。
でも結構です。オリジナルのFoo
がどのように使用されるかを想像します。 Bar
がFoo
を作成する方法で作成されたBar
が必要だとしましょう。ああ、そしていくつかのQuz
が必要です。だから私はこれをします:
$foo = new Foo(1,10);
$bar = $foo.bar;
$quz1 = $foo.calc();
$quz2 = $foo.calc();
$quz3 = $foo.calc();
それはすべて構築コードです。それが私を買ったすべてはこのようにそれを書く必要がなかった:
上記のクラスをDIありでファクトリなしで実装するにはどうすればよいですか?
$min = 1;
$max = 10;
$bar = new Bar(new Quz(Rand($min, $max));
$quz1 = new Quz(Rand($min, $max));
$quz2 = new Quz(Rand($min, $max));
$quz3 = new Quz(Rand($min, $max));
私は神に誓います、実際には読みやすいです。また、驚くほど柔軟です。好きなものを注射します。 $ minと$ maxで好きな方法を使用します。
しかし、それでは、改善されたFoo
に切り替えたときに何が得られるかを見てみましょう。
$foo = new Foo(new BarFactoryDefault(), new QuzFactoryDefault());
$foo.init(1,10);
$bar = $foo.bar;
$quz1 = $foo.calc();
$quz2 = $foo.calc();
$quz3 = $foo.calc();
それとも私はそれをひどく間違っていますか?
はい
上記のいずれのソリューションよりも複雑であることに加えて、これらのデフォルトのファクトリクラスはもちろん、それらのインターフェイスもまだ作成していません。はい、これはひどく間違っています。
あなたが聞いたことのあるパターンやテクニックに手を伸ばすことは素晴らしく、良い食材を使うことで良い料理を作ろうと思ったのです。あなたがそれを提供する前に、常にあなたが調理する食べ物を味わってください。ケチャップと牛乳が好きです。牛乳のケチャップは好きではありません。それはケチャップでも牛乳でも問題ではありません。
この例の問題は貧血です。これらのテクニックが解決する問題は、この例にはありません。したがって、ここでテクニックを探求しようとすることは、あなたに良いことを何も教えない、オーバーエンジニアリングのフェスティバルになります。
あなたが次に扱う他のいくつかの質問をしましたが、私がここで作りたい主要なポイントは、それらがどのように使用されるかを考えずにこれらのクラスを設計しないことです。
依存性注入を行うときにファクトリは必要ですか?
番号。
依存性注入には工場は必要ありません。工場は多くの建設パターンの1つです。依存性注入には、構築パターンさえ必要ありません。メインでそれを行うことができます。工場は注射を行うかもしれません。工場に注入することができます。工場は建設の詳細を抽象化します。ほとんどの場合、ファクトリーを使用して、妥当なデフォルトとして扱いたい依存関係を非表示にします。少なくとも私はそうします。
各クラスのファクトリメソッドまたはクラスが本当に必要ですか?
あなたが見るのにうんざりしている建設式ごとにファクトリーメソッドが必要です。ここに1対1の関係があると考えるのはおかしいです。それはゼロから多くです。
コンストラクタを置き換えるために本当にinit()関数が必要ですか?
初期化されていない状態でFoo
を存在させることは許されない罪だと思います。これを修正する方法はたくさんあります。それをコンストラクタに折りたたみます。これを2つのオブジェクトに分割します。ビルダーのパターン。儀式を守らないと、私が使用できないものを押し付けないようにする方法を見つけてください。
申し訳ありませんが、これはそのような怒りに変わりました。デザインパターンを調べて、何が大騒ぎなのかと思ったのを覚えています。このような悪い例は、それを得るのにとても時間がかかった大きな理由でした。なぜそれが良いのかがわかるまでは、これが良いことを受け入れないでください。ここで信仰はキラーです。あなたがそれを提供する前に、食べ物を味わってください。