web-dev-qa-db-ja.com

依存性注入を行うときにファクトリは必要ですか?

私はいくつかのコードをリファクタリングしようとしているので、依存関係注入を使用しています。

次の(意味のない)サンプルクラスを見てください。

_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()関数が必要ですか?
  • それとも私はそれをひどく間違っていますか?
3
Arnold Daniels

各クラスのファクトリメソッドまたはクラスが本当に必要ですか?

「Bar」と「Quz」のインスタンスを作成しているので、それらを分離するためにファクトリーを使用することは理にかなっています。

ただし、コンストラクタでBarを作成するだけなので、ファクトリを取得できず、代わりにBarのインスタンスを取得します。そうすることは、ファクトリなしの依存性注入の例です。これにより、クライアントコードがオブジェクトを提供できるようになります。意図がある場合とそうでない場合は、ファクトリを使用します。実際には、Barを使用していないため、スキップできます。

編集:calcの結果を使用してBarを作成したいと思います。 Barインスタンスの代わりにファクトリを使用すると、BarQuzに対して有効な結果で作成され、minmaxが指定されていても、barがパブリックであり、クライアントコードがそれを上書きできるため、カプセル化が向上します。その保護に対抗します。

Quzに従って、クライアントコードがcalcを呼び出すたびに新しいインスタンスを作成するため、より多くのインスタンスを取得できるようにファクトリが必要になります。

コンストラクタを置き換えるために本当にinit()関数が必要ですか?

コンストラクターでファクトリーを取ることができます。別のメソッドを使用すると、開発者がコンストラクターを呼び出してinitの呼び出しをスキップできるため、問題が発生します。

ここで、ファクトリを非表示にして、クライアントコードで同じファクトリを共有する複数のインスタンスを作成する場合は、そのためのファクトリを作成できます。私はそれをする必要がないと言わざるを得ません。

それとも私はそれをひどく間違っていますか?

initをコンストラクターから分離します。クライアントコードがインスタンスを作成し、calcを呼び出さずにinitを呼び出すとどうなるかを考えてみましょう。コードでは、フィールドが初期化されなかったため(言語によって)、乱数は0(またはデフォルト値)またはゴミ箱になります。

最悪の場合、Barを実際に使用した場合、initで初期化されているため初期化されていないため、無効なポインターになります。

5
Theraot

オブジェクトまたはファクトリーを注入すると、さまざまな目的が解決され、さまざまな意図が伝達されます。

ファクトリを渡すと、新しいオブジェクトはそのファクトリから新しいオブジェクトを作成する場合と作成しない場合があります。これらのオブジェクトを事前に構成することはできません(少なくとも、ファクトリで静的プロパティを設定するようなコードの匂いがない限りは)。注入できるのは、工場があるものだけです。時々、これはまさにあなたが望むものであるかもしれません-しかし、あなたはたまたま工場である普通のオブジェクトを注入するのと同じようにそれを見ることができます。

「通常の」オブジェクトを挿入する場合、何でも渡すことができます-ファクトリのないオブジェクトは問題ありません。その目的のためだけにファクトリークラスを作成することは賢明ではありません。

なぜコードインジェクトをしたいのかと自問する場合、おそらくconfiguration/state/behavior/whatever-you-call-itを渡したいでしょう。受け継がれる工場はそれを非常に制限します。たとえば、構成できなかったデータベースコネクタをどのように渡すことができますか?また、再利用もできませんでした。

あなたはここでコンセプトを混同していると思います(つまり、ファクトリーパターンと依存性注入)-一緒に使用できますが、そうする必要はありません。

3
Eiko

通常、次の2つの条件が満たされた場合にファクトリを使用することは理にかなっています。

  1. コンパイル時に依存関係がわからない
  2. これらの依存関係を設定するプロセスは「複雑」です

これらの2つの基準が満たされていない場合、コンストラクターを介してインスタンスを渡すだけで十分に機能し、費用対効果が最も高くなります。

ファクトリー自体の基本原則は基準2であるため、それがない限り、ファクトリーを使用することも、DIを使用することも意味がありません。

2
whatsisname

オリジナルのFooは3つのことを行います:

  1. $this->bar = new Bar(new Quz(Rand($min, $max))
  2. calc()new Quz(Rand($min, $max))として定義します
  3. 覚えている$minおよび$maxしたがって、calcはそれらを渡す必要はありません

オリジナルのFooはすでに依存性注入を行っています。それは主に建設です。 Randは動作のほんの一部であり、動作と構造を分離したいので、構造にハードコーディングされていることは少し奇妙ですが、私の主な不満ではありません。

私の主な不満は、これが慣れているのを見ていません。これは不自然な例のように読めます。私はこれのどれもが助けになっていることを本当に見ることができません。

でも結構です。オリジナルのFooがどのように使用されるかを想像します。 BarFooを作成する方法で作成された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つのオブジェクトに分割します。ビルダーのパターン。儀式を守らないと、私が使用できないものを押し付けないようにする方法を見つけてください。

申し訳ありませんが、これはそのような怒りに変わりました。デザインパターンを調べて、何が大騒ぎなのかと思ったのを覚えています。このような悪い例は、それを得るのにとても時間がかかった大きな理由でした。なぜそれが良いのかがわかるまでは、これが良いことを受け入れないでください。ここで信仰はキラーです。あなたがそれを提供する前に、食べ物を味わってください。

1
candied_orange