20

PHP でControllerクラスを作成する際の問題を解決する方法は次のとおりです。

  • 依存性注入を採用することで簡単にテストできます。
  • 最終プログラマーに共有オブジェクトを提供する
  • 新しいユーザー ライブラリをロードする方法を提供する

依存性注入フレームワークを使用したコントローラーのインスタンス化については、下を見てください


問題は、派生コントローラーが、プログラマーが必要とするあらゆるリソース (フレームワークが提供するものなど) を使用できることです。共有リソース (DB、ユーザー、ストレージ、キャッシュ、ヘルパー)、ユーザー定義のクラス、または別のライブラリへの統合アクセスを作成する方法は?

エレガントなソリューション?

私の問題にはいくつかの解決策がありますが、どちらもエレガントではないようです

  • コンストラクターによってすべての共有オブジェクトを渡そうとしますか? (10 個のプレースホルダーがあってもコンストラクターを作成する場合があります)
  • ゲッター、セッターを作成しますか? (肥大化したコード)$controller->setApplication($app)
  • 共有リソースにシングルトンを適用しますか? User::getInstance()またDatabase::getInstance()
  • コントローラー内でオブジェクトを共有するためのシングルトンとして依存性注入コンテナーを使用しますか?
  • ファクトリとして1 つのグローバル アプリケーション シングルトンを提供しますか? (これはphpフレームワークで非常に使用されているように見えますが、DIの原則とデメテルの法則に強く反しています)

強く結合されたクラスを作成することは推奨されておらず、追放されていることは理解しています:)、しかし、このパラダイムが他のプログラマー(コントローラークラス)の出発点にどのように適用されるかはわかりません。提供された共有リソースにアクセスできる必要がありますMVC アーキテクチャに。コントローラー クラスを小さなクラスに分割すると、MVC の実際的な意味が何らかの形で破壊されると思います。


依存性注入フレームワーク

DI フレームワークは実行可能な選択肢のようです。ただし、問題は引き続き発生します。Controller のようなクラスは Application 層ではなく、RequestHandler/Response 層に存在します。

このレイヤーはコントローラーをどのようにインスタンス化する必要がありますか?

  • このレイヤーにDIインジェクターを渡しますか?
  • シングルトンとしてのDIフレームワーク?
  • このレイヤーのみに分離された DI フレームワーク構成を配置し、別の DI インジェクター インスタンスを作成しますか?
4

5 に答える 5

3

フレームワークを自分で開発していますか?そうでない場合は、既存のフレームワークとその既存のソリューションから選択する必要があるため、質問は当てはまりません。この場合、質問は「フレームワーク X で単体テスト/依存性注入を行う方法」のように再定式化する必要があります。

自分でフレームワークを開発している場合は、既存のフレームワークがこの問題にどのようにアプローチしているかを最初に確認する必要があります。また、独自の要件を詳しく説明し、可能な限り単純なソリューションを採用する必要もあります。要件がなければ、あなたの質問は純粋に審美的で論争的です。

私の謙虚な意見では、最も簡単な解決策は、フレームワークによって提供されるデフォルトに初期化するパブリック プロパティを持つことです。それ以外の場合は、ここにモックを挿入できます。(これは、ゲッター/セッターのソリューションと同じですが、前述の肥大化はありません。ゲッターとセッターが常に必要なわけではありません。)オプションで、本当に必要な場合は、1回の呼び出しでそれらを初期化するコンストラクターを提供できます(提案したように) .

シングルトンは洗練されたソリューションですが、繰り返しになりますが、自分の場合に適用できるか自問する必要があります。アプリケーションで同じタイプのオブジェクトの異なるインスタンスが必要な場合、それを使用することはできません (たとえば、アプリケーションの半分だけでクラスをモックしたい場合)。

もちろん、すべてのオプションがあることは本当に素晴らしいことです。ゲッター/セッター、コンストラクターを使用できます。初期化が省略されている場合、デフォルトはシングルトン ファクトリから取得されます。しかし、必要のないときにオプションが多すぎるのは素晴らしいことではありません。プログラマーが使用する規則、オプション、およびパターンを把握しなければならないため、厄介です。単純な CRUD を実行するためだけに、何十もの設計上の決定を下したくはありません。

他のフレームワークを見ると、特効薬がないことがわかります。多くの場合、1 つのフレームワークで、コンテキストに応じてさまざまな手法が使用されます。コントローラーでは、DI は非常に単純なものです。適切な変数をコントローラー クラスに挿入するように指示する CakePHP の $helpers、$components 変数を見てください。アプリケーション自体は、常に 1 つのアプリケーションしか存在しないため、シングルトンは依然として良いことです。あまり変更/モックされないプロパティは、パブリック プロパティを利用して注入されます。MVC の場合、CakePHP の AppController、AppView、AppModel と同様に、サブクラス化も完全に実行可能なオプションです。これらは、フレームワークとすべての特定の Controller、View、および Model クラスの間のクラス階層に挿入されます。

Java では、動的クラス ローダーとリフレクションにより、さらに多くのオプションから選択できます。しかしその一方で、並列リクエスト、ワーカー スレッド間の共有オブジェクトと状態、分散アプリ サーバーなど、さらに多くの要件もサポートする必要があります。

そもそも何が必要かを知っている場合にのみ、何が自分に適しているかという質問に答えることができます. しかし実際には、なぜ別の新しいフレームワークを作成するのでしょうか?

于 2010-02-14T20:07:56.957 に答える
1

私が理解している限り、あなたの Application クラスはディスパッチャーでなければなりません。もしそうなら、コントローラー コンストラクターを使用してアプリケーションのインスタンスを渡し、コントローラーが呼び出し元を認識できるようにします。後で、コードが CLI 内から呼び出されるかどうかに応じて別の Application インスタンスが必要な場合は、Application\Http および Application\Cli が実装する ApplicationInterface を使用でき、すべてを簡単に維持できます。

DI の優れた実装を取得するために、いくつかのファクトリ パターンを実装することもできます。たとえば、https ://github.com/troelskn/bucket/blob/master/lib/bucket.inc.php で createThroughReflection メソッドを確認してください。

これが理にかなっていることを願っています。

よろしく、ニック

于 2011-01-19T07:31:46.443 に答える
1

依存性注入が実行可能である場合、シングルトンは眉をひそめられます (そして、シングルトンが必要なケースをまだ見つけていません)。

ほとんどの場合、コントローラーのインスタンス化を制御できるため、前述$controller->setApplication($application)の . つまりController::setApplication()、インスタンス メソッドを介して静的変数にアクセスします。

例えば:

// コントローラー内でアプリケーションを定義します -- おそらくブートストラップで
$application = 新しいアプリケーション();
コントローラー::setApplication($アプリケーション);

// Controller クラス定義内のどこか
パブリック関数 setContentType($contentType)
{
    self::$application->setContentType($contentType);
}

私は、静的およびインスタンスのプロパティとメソッドを分離する習慣を身につけました (必要に応じて、プロパティをクラス定義の先頭にグループ化します)。クラスは依然として非常にコンパクトなままであるため、これはシングルトンを使用するよりも扱いにくいと感じています。

于 2010-02-06T01:14:11.963 に答える
1

アプリケーションまたはルーター/ディスパッチャーに与える ControllerFatory を使用することもできます

つまり、$controllerFactory->createController($name); を呼び出すことができます。

アプリケーションは、ファクトリが作成するコントローラーを作成する方法を知りません。独自の ControllerFactory を DI コンテナーに挿入できるため、コントローラーに応じて必要なすべての依存関係を管理できます。

class ControllerFactory {
    public function __construct(EvenDispatcher $dispatcher,
                                Request $request,
                                ResponseFactory $responseFactory, 
                                ModelFactory $modelFactory, 
                                FormFactory $formFactory) {
        ...
    }

    public function createController($name = 'Default') {
        switch ($name) {
            case 'User':
              return new UserController($dispatcher, 
                                        $request, 
                                        $responseFactory->createResponse('Html'), 
                                        $modelFactory->createModel('User'),
                                        $formFactory->createForm('User'),...);

              break;
            case 'Ajax':
              return new AjaxController($dispatcher, 
                                        $request, 
                                        $responseFactory->createResponse('Json'), 
                                        $modelFactory->createModel('User'));
              break;
            default:
                 return new DefaultController($dispatcher, $request, $responseFactory->createResponse('Html'));
        }
    }

} 

したがって、このファクトリを DI コンテナーに追加して、アプリケーションに渡すだけです。新しいコントローラーが必要なときはいつでもそれをファクトリーに追加し、新しい依存関係が必要な場合は、DI コンテナーを介してそれらをファクトリーに提供します。

class App {
    public function __construct(Router $router,Request $request, ControllerFactory $cf, ... ) {
      ...
    }

    public function execute() {
        $controllerName = $this->router->getMatchedController();
        $actionName $this->router->getMatchedAction();

        $controller = $cf->createController($controllerName);

        if(is_callable($controller, $actionName)) {
            $response = $controller->$action(request);
            $response->send();     
        }
    }
}

これは製品コードではなく、テストしていませんが、これはコントローラーをアプリケーションから切り離す方法です。ただし、コントローラーが応答を返し、アプリで応答を実行するため、ここには 1 つの不適切な結合があることに注意してください。しかし、私が言ったように、これはほんの一例です。

モデル、フォーム、およびコントローラのファクトリをそれぞれの親に渡すことは、通常は良い考えです。これは、ブートストラップ時にすべてのオブジェクト グラフをロードすることになり、非常に悪く、メモリを消費するためです。

この回答がすでに承認されていることは知っていますが、この件に関しては私の 2 セントです

この件については良い記事があります

http://miller.limethinking.co.uk/2011/07/07/dependency-injection-moving-from-basics-to-container/

于 2011-11-05T19:29:17.593 に答える
1

リファクタリングはどうですか?

確かにそれはあなたの選択肢の1つではありませんでしたが、コードは主に結合されたクラスであると述べています。この時間と労力をかけて、よりモジュール化されたテスト可能なコンポーネントにリファクタリングしてみませんか?

于 2010-02-11T19:36:44.000 に答える