独自のそれぞれのコントローラーを取得する必要があるものを決定する方法は?


10

PHPで構築したWebアプリケーションでMVCパターンを使用しています。

一連のアクションに新しい専用コントローラーが必要か、それとも既存のコントローラー内に配置する必要があるかを判断するのに常に苦労しています。

コントローラーを作成する際に従うべき良い経験則はありますか?

例えば、私は持つことができます:

AuthenticationController アクションあり:

  • index() ログインフォームを表示します。
  • submit() フォームの送信を処理します。
  • logout()、自明です。

または

LoginController アクションあり:

  • index() ログインフォームを表示します。
  • submit() フォームの送信を処理します。

LogoutController アクション付き:

  • index() ログアウトを処理します。

または

AccountController アクションあり:

  • loginGet() ログインフォームを表示します。
  • loginPost() ログインフォームの送信を処理します。
  • logoutGet() ログアウトを処理します。
  • registerGet() 登録フォームを表示します。
  • registerPost() フォームの送信を処理します。

    また、アカウントに関連するその他のアクション。


たぶんRESTfulなデザインを見てください。この種のすべての問題を解決するわけではありませんが、それについて考える方法を非常に良い方向に導きます。
thorstenmüller2014

回答:


3

コントローラの適切なグループを見つけるために、テストを考えてみてください

(実際にテストを行わなくても、コントローラーのテストをどのように行うかを考えると、コントローラーの構造について非常に優れた洞察が得られます。)

AuthenticationControllerそれだけでログインするための機能が含まれており、ログアウトが、あなたのテストコードは何とかそれが成功したログインをテストすることができます前に、テスト目的のために偽のアカウントを作成する必要がありますので、それだけでテスト可能ではありません。テスト対象のサブシステムをバイパスして、モデルに直接移動してテストアカウントを作成できますが、脆弱なテストを手にすることになります。モデルが変更された場合は、どのテストをコード化するだけでなく、コードを変更する必要がありますモデルだけでなく、コントローラーのインターフェイスと動作が変更されていない場合でも、コントローラーをテストするコードも含まれます。それは無理です。

LoginController同じ理由でA は不適切です。最初にアカウントを作成せずにテストすることはできません。たとえば、重複したログインを防止して、ログアウト後にユーザーがログインできるようにするなど、テストできないことがさらにあります。(このコントローラーにはログアウト機能がないため。)

AccountControllerあなたのテストを行うために、あなたが必要なすべてを提供します:あなたは、ログインしようとし、テストアカウントを作成することができ、アカウントを削除することができ、その後、あなたはログインもう、あなたがパスワードを変更していることを確認することができないことができ作りますログインなどのために正しいパスワードを使用する必要があります

結論としては、最小のテストスイートを作成するためにも、のすべての機能をAccountController利用できるようにする必要があります。それをより小さなコントローラーに細分すると、適切なテストには機能が不十分なハンディキャップのコントローラーが生成されるようです。これは、の機能AccountControllerが意味のある最小のサブディビジョンであることを非常によく示しています。

そして一般的に言えば、「テストを考える」アプローチは、この特定のシナリオだけでなく、将来遭遇する同様のシナリオでも機能します。


1

答えはそれほど明白ではありません

回答を述べる前に、いくつかはっきりさせておきます。まず第一に:

コントローラとは何ですか?

コントローラーは、ディスパッチ後の要求を制御するシステムの一部です。したがって、それを関連するアクションのセットとして定義できます...何ですか?

コントローラのスコープは何ですか?

そして、それは多かれ少なかれ私たちが答えを出すときの一部です。どう思いますか?それは物事のコントローラー(たとえば、アカウント)またはアクションのコントローラーですか?もちろん、そのモデルのコントローラ、またはアクションを提供するより抽象的なもののコントローラです。

答えは...

アクション付きのAuthenticationController:

  • ログインフォームを表示するindex()。
  • submit()は、フォーム送信を処理します。
  • logout()、説明不要です。

いや、認証はプロセスです。そのように行かないでください。

アクション付きのLoginController:

  • ログインフォームを表示するindex()。
  • submit()は、フォーム送信を処理します。

こっちも一緒。ログイン-アクション。アクションコントローラーを作成しない方がよい(それに相関モデルがない)。

アクションを持つAccountController:

  • loginGet()を使用して、ログインフォームを表示します。
  • ログインフォームの送信を処理するためのloginPost()。
  • ログアウトを処理するlogoutGet()。
  • registerGet()を使用して、登録フォームを表示します。
  • フォーム送信を処理するregisterPost()。

かなり良いですが、その低レベルのコントローラー(コントローラーは抽象化そのものです)を構築する価値があるとは思いません。とにかく、* Getまたは* Postを使用してメソッドを作成することは不明確です。

なにか提案を?

はい、考慮してください:

AccountController:

  • login(AccountModel)
  • logout(AccountModel)
  • register(AccountModel)
  • index()

そしてそれに関連するモデル、ofc Accountクラス。これにより、モデルとコントローラーのペアを別の場所に移動し(必要な場合)、明確なコードを作成することができます(login()メソッドの意味が明らかです)。モデルへのこだわりは、特にCRUDアプリケーションで非常に有名です。


1

コントローラは通常、特定のリソース(エンティティクラス、データベース内のテーブル)に対して作成されますが、アプリケーションの特定の部分に関与するアクションをグループ化するために作成することもできます。あなたの例では、それはアプリケーションのセキュリティを処理するコントローラです:

class SecurityController
{
    // can handle both the login page display and
    // the login page submission
    login(); 

    logout();

    register();

    // optional: confirm account after registration
    confirm();

    // displays the forgot password page
    forgotPassword();

    // displays the reset password page
    // and handle the form submission
    resetPassword();
}

:セキュリティ関連のアクションとユーザープロファイルアクションを同じコントローラに配置しないでください。それらはユーザーに関連しているので意味があるかもしれませんが、1つは認証を処理し、もう1つは電子メール、名前などの更新を処理する必要があります。

リソース用に作成されたコントローラー(例:)Taskを使用すると、通常のCRUDアクションが実行されます。

class TasksController
{
    // usually displays a paginated list of tasks
    index();

    // displays a certain task, based on an identifier
    show(id);

    // displays page with form and
    // handles form submission for creating
    // new tasks
    create();

    // same as create(), but for changing records
    update(id);     

    // displays confirmation message
    // and handles submissions in case of confirmation
    delete()
}

もちろん、関連するリソースを同じコントローラーに追加する可能性があります。たとえば、エンティティBusinessがあり、それぞれに複数のBusinessServiceエンティティがあるとします。そのためのコントローラーは次のようになります。

class BusinessController
{
    index();

    show(id);

    create();

    update(id);

    delete();

    // display the business services for a certain business
    listBusinessServices(businessId);

    // displays a certain business service
    showBusinessService(id);

    // create a new business service for a certain business
    createBusinessService(businessId);

    // updates a certain business service
    updateBusinessService(id);

    // deletes a certain business service
    deleteBusinessService(id);
}

このアプローチは、関連する子エンティティが親エンティティなしでは存在できない場合に意味があります。

これらは私の推奨事項です:

  • 関連する操作のグループに基づいてコントローラーを作成します(セキュリティなどの特定の責任の処理、またはリソースに対するCRUD操作など)。
  • リソースベースのコントローラーの場合、不要なアクションを追加しないでください(リソースを更新する予定がない場合は、更新アクションを追加しないでください)。
  • 「カスタム」アクションを追加して、物事を簡素化できます(たとえば、Subscriptionエンティティの数が限られたエントリに基づいて可用性を持っている場合、という名前のコントローラに新しいアクションを追加use()して、から1つのエントリを減算するという単一の目的を持つことができますSubscription
  • 物事をシンプルに保つ-膨大な数のアクションと複雑なロジックでコントローラーを雑然としないでください。アクションの数を減らすか、2つのコントローラーを作成することで物事を簡素化してください。
  • MVCに重点を置いたフレームワークを使用している場合は、それらのベストプラクティスガイドラインに従ってください(ある場合)。

ここでさらに読むためのいくつかのリソース。


0

2つの敵対的な設計の「力」が見られます(コントローラーに限定されません)。

  • 凝集性-コントローラーは関連するアクションをグループ化する必要があります
  • シンプルさ-複雑さを管理するために、コントローラーはできるだけ小さくする必要があります

凝集性の観点から、3つのアクション(ログイン、ログアウト、登録)はすべて関連していますが、ログインとログアウトは登録よりもはるかに重要です。それらは意味的に関連しており(一方は他方の反転です)、おそらく同じサービスオブジェクトも使用します(それらの実装もまとまりがあります)。

最初のヒントは、ログインとログアウトを1つのコントローラーにグループ化することです。しかし、ログインとログアウトのコントローラーの実装がそれほど単純ではない場合(たとえば、ログインにキャプチャが含まれている、認証方法が多いなど)、それらをLoginControllerとLogoutControllerに分けて単純さを維持しても問題ありません。この複雑さのしきい値(コントローラーの分割を開始する必要がある場合)がどこにあるかは、少し個人的なものです。

また、コードを最初に設計する場合は、変更に応じてコードをリファクタリングできることを忘れないでください。この場合、シンプルな設計(AuthenticationControllerが1つ)から始めるのは非常に一般的であり、時間が経つと、コードを複雑にする要件が増えることになります。複雑度のしきい値を超えたら、2つのコントローラーにリファクタリングする必要があります。

ところで、あなたのコードはあなたがGETリクエストでユーザーをログアウトしていることを示唆しています。HTTP GETはnullipotentであるべきです(アプリケーションの状態を変更してはいけません)。


0

以下にいくつかの経験則を示します。

  • サブジェクトまたはトピック別に整理します。コントローラー名はトピックの名前です。

  • コントローラの名前はURLに表示され、ユーザーに表示されるため、できればユーザーが理解できるようにしてください。

あなたが言及する状況(認証)で、MVCチームはすでにコントローラーを作成しています。Visual Studio 2013を開いてクリック

File / New / Project... 
Search installed templates for "ASP.NET MVC4 Web Application"
Choose "Internet Application" / OK.

AccountController.csには、ユーザーアカウントを管理するためのすべてのメソッドが含まれています。

Login()
Logoff()
Register()
Disassociate()
Manage()
ExternalLogin()

そのため、「アカウント」というトピック名が表示されるように、「ユーザーアカウントと認証」というトピックで整理されています。


0

用語

HTTP関連のメソッドを含むクラスを「コントローラー」と呼ぶのは大きな誤解だと思います。

コントローラはリクエストを処理するメソッドですが、そのようなメソッドを含むクラスではありません。だから、index()submit()logout()コントローラです。

この種のメソッドを含むクラスは、コントローラーのグループを構成し、「最下位」の名前空間の役割を果たすため、「コントローラー」と呼ばれます。FP言語(Haskellのような)では、それは単なるモジュールです。サービスや他のプログラム全体に関するものを除き、OOP言語ではこれらの「コントローラー」クラスをできるだけステートレスに保つことをお勧めします。

答え

用語を整理すると、「コントローラーを名前空間/モジュールにどのように分離する必要があるか」という疑問が生じます。答えは1つです。単一の名前空間/モジュール内のコントローラー、同じ種類のデータを処理する必要があります。例えば、UserController扱うのインスタンスとUserクラスが、時折、必要に応じて、その他の関連する事柄に触れます。

以来loginlogout他のそのような行動は、ほとんどのセッションを扱っていると、それはおそらく最良の内側にそれらを置くためにSessionController、そしてindexちょうどフォームを印刷し、コントローラは、の中に配置する必要がありLoginPageController、それは明らかに、ログインページを扱っているので、。HTMLレンダリングとセッション管理を単一のクラスに入れることは少し意味があり、それはSRPおよびおそらく他の多くの優れた慣行に違反します。

一般原則

コードの配置場所を決めるのに問題がある場合は、処理するデータ(およびタイプ)から始めます。


2
申し訳ありませんが、これらはコントローラではなくアクションです:)
JK01

@ JK01それらはあなたがそれらを呼ぶものです。用語ですよね。そして、名前空間/モジュールですでに十分なので、それらをクラスに整理しない多くのフレームワークがあるので、それらの関数を「コントローラー」(または「ハンドラー」)と呼ぶフレームワークがあります。あなたは好きな言葉を使うことができます、それは単なる言葉ですが、私は言葉が少ない方がいいと思います。
スクリプト
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.