9

私は小さなフレームワークを持っていて、このようにコーディングしました。それが依存性注入と呼ばれるかどうかはわかりません。デザインパターンのようなものかどうかはわかりません。$thisまた、paramとして渡すことが悪い習慣であるかどうかもわかりません。

これを見てください。(実際の例ではありません。説明のためにこれらのコードをブラウザーに書き込んだだけです。)

/* This is engine model */
require_once('Database.class.php');
require_once('Image.class.php');
require_once('Misc.class.php');
require_once('BBCode.class.php');

class FrameWork_Engine_Model
{
    public $database, $config, $misc, $bbcode, $controller, $image;

    function __construct($config)
    {
            $this->database = new Database($configParams);
            $this->image = new Image($this);
            $this->misc = new Misc($this);
            $this->bbcode = new BBCode($this);
            $this->controller = new Controller($this); //here I call Register controller depending on routing, in this case, register controller.
    }
 ...
 }

 /* This is register controller */
 class Register extends Base_Controller
 {
       /*I can access anything over Engine Model in my controllers */
       $this->engine->database->query(); //I access database model
       $this->engine->bbcode->tag('you'); //I access bbcode model
       $this->engine->image->sanitizeUploadedFile(); //I access image model

       //etc. I can access others models like this.
 }

基本的に、私のコントローラーはエンジンモデルを介して任意のモデルにアクセスできます。同様に、私dependency injection is all about injecting dependencies into controllers?のレジスタコントローラが機能するには、データベースモデル、ルーティングモデル、およびテンプレートモデルが必要だと思います。ここにそれが依存するすべてがあります。私は間違っていますか?

そうは言っても、私の質問は次のとおりです。

  1. 有効な依存性注入の例ですか?そうでない場合、それは何ですか?デザインパターンに名前はありますか?

  2. 依存性注入とは何の関係もない場合、DIになるためにどのような変更を行う必要がありますか?

  3. $this新しく作成されたクラスにパラメーターを渡すことは悪い習慣ですか?もしそうなら、なぜですか?

追伸 トピックで3つの質問をすることは、stackoverflowが好きなことではないことは知っていますが、テキスト全体をコピーして貼り付けて質問したくありません。

4

2 に答える 2

19

もうすぐです。

質問1

いいえ、それを有効な依存性注入の例とは見なしていません。これは、サービスロケーターに少し似ています(コンテナー全体をサービスに注入し、それを使用して依存サービスを「検索」するため)。

質問2

依存性注入と依存性注入コンテナの間で少し混乱しています。

まず、依存性注入とは、依存関係を作成/プルするのではなく、実行時にオブジェクトにプッシュすることを意味します。

これを例示するには:

//hardcoded dependecies
class BadService
{
    public function __construct() 
    {
        $this->dep1 = new ConcreteObject1();
        $this->dep2 = new ConcreteObject2();
    }
}

したがって、上記の例では、BadService他の依存関係はコンストラクター自体にすでにハードプルされているため、実行時に他の依存関係をワイヤリングすることはできません。

//service locator pattern
class AlmostGoodService
{
    public function __construct(Container $container)
    {
        $this->dep1 = $container->getADep1();
        $this->dep2 = $container->getADep2();
    }
}

このAlmostGoodService例では、前の例からハード依存関係を削除しましたが、コンテナーの特定の実装に依存しています(つまり、そのコンテナーの実装を提供しないと、サービスを再利用できません)。これはあなたがしていることに一致する例です。

//dependecy injection    
class GoodService
{
    public function __construct($dep1, OptionalInterface $dep2)
    {
        $this->dep1 = $dep1;
        $this->dep2 = $dep2;
    }
}

このサービスは、具体的な依存関係の作成に関係せず、実行時に、またはの「プロトコル」またはのOptionalInterfaceを実装する依存関係(したがって、制御の反転の名前-背後にある基本的な概念GoodService)を使用して簡単に「配線」できます。依存性注入)。$dep1$dep2

この配線を行うコンポーネントは、依存性注入コンテナと呼ばれます。

現在、依存性注入コンテナは、最も単純な形式では、何らかの形式の構成に基づいて実行時にオブジェクトを接続できるオブジェクトにすぎません。

私はあなたがほとんどそこにいると言いましたが、あなたの実装にはいくつかの問題があります:

  • 配線は怠惰である必要があります(アプリケーションが大きくなるにつれてアプリケーションの速度が大幅に低下するため、コンストラクターですべてを機能させたくはありません)
  • コンテナ全体($this)を依存関係として渡さないでください。これは、制御のより弱い反転、つまりサービスロケーターにフォールバックするためです。代わりに、具体的な依存関係をサービスコンストラクターに渡す必要があります

質問3

全体をサービス(つまり、コントローラーまたは怠惰なサービスファクトリー)への依存関係として渡したい場合がありますが$container、サービスがより再利用可能になるため、通常はこの方法を避けた方がよいでしょう。テストが簡単です。サービスの依存関係が多すぎると感じた場合は、サービスの実行が多すぎることを示す良い兆候であり、サービスを分割する良い機会です。

プロトタイプコンテナの実装

したがって、上記の私の回答に基づいて、ここに改訂された(完璧にはほど遠い)実装があります:

/* This is the revised engine model */
class FrameWork_Engine_Model
{
    function __construct($config)
    {
            $this->config = $cofig; 
    }

    public function database()
    {
        require_once('Database.class.php');
        return new Database($this->config['configParams']);
    }

    public function bbcode()
    {
        require_once('BBCode.class.php');
        return new BBCode($this->database());
    }

    public function image()
    {
        require_once('Image.class.php');
        $this->image = new Image($this->config['extensionName']);
    }
    ....

    public function register_controller($shared = true)
    {
        if ($shared && $this->register_controller) {
          return $this->register_controller;
        }

        return $this->register_controller = new Register_Controller($this->database(), $thus->image(), $this->bbcode());
    }
 }

今、あなたのサービスを使用するには:

$container = new FrameWork_Engine_Model(); 
$container->register_controller()->doSomeAction()

何を改善できるでしょうか?コンテナは次のことを行う必要があります。

  • サービスを共有する方法を提供します。つまり、サービスを1回だけ初期化します。
  • ロック可能-構成後にロックする方法を提供します
  • 他のコンテナと「マージ」できる-そうすれば、アプリケーションは本当にモジュール化されます
  • オプションの依存関係を許可する
  • スコープを許可する
  • タグ付けサービスをサポートする

DIコンテナの実装を使用する準備ができました

これらはすべて、依存性注入に関する明確なドキュメントが付属しています

于 2013-03-10T16:41:59.883 に答える
6
  1. あなたFrameWork_Engine_Modelはレジストリです(レジストリパターン)。レジストリを依存性としてすべてのオブジェクトに注入することは、誤解されている依存性注入の一種です。技術的にはDIですが、すべてからすべてへの依存関係を作成し、DIが提供する必要のある柔軟性も奪います。
  2. サービスをインスタンス化し、それらの依存関係を管理することを目的としている場合は、制御の反転(DIに関連する一般的なパターン)にFrameWork_Engine_Model変更できます。
  3. いいえ、一般的ではありません。

この質問の範囲内ではないと思うので、クラス名の選択とサービスとコントローラーの責任については議論しません。ただの注意:あなたのコントローラーはやりすぎのようです。クリーンなコードに関心がある場合は、単一責任の原則を確認し、コントローラーを「薄型」に保ち、ビジネスロジックとデータベースクエリをサービスレイヤーに移動し、bbcodeなどのメカニズムをビューに出力することをお勧めします。

それで、あなたの例と、それを依存性注入の賢明な使用法に変更する方法に戻ってください。プリミティブIoCコンテナは次のようになります。

public function createRegisterController()
{
    $controller = new RegisterController();
    $controller->setImage($this->getImageService());
    // ...
    return $controller;
}
public function getImageService()
{
    if ($this->imageService === null) {
        $this->imageService = new Image();
        // inject dependencies of Image here
    }
    return $this->imageService;
}

ここで重要な点は、必要な依存関係のみを注入することです。また、DIを装ったグローバル変数の束を作成しないでください。

于 2013-03-10T16:40:51.723 に答える