web-dev-qa-db-ja.com

「MVC」の「コントローラー」には何が入りますか?

MVCの基本的な概念を理解していると思います。モデルにはアプリケーションのデータと動作が含まれ、ビューにはユーザーに表示する役割があり、コントローラーはユーザー入力を処理します。私が不確かなのは、正確にはwhatがControllerに入ることです。

たとえば、かなり単純なアプリケーションがあるとしましょう(Javaを具体的に考えていますが、同じ原則が他の場所にも当てはまると思います)。 app.modelapp.viewapp.controllerという3つのパッケージにコードを整理します。

app.modelパッケージ内には、アプリケーションの実際の動作を反映するクラスがいくつかあります。これらのextends Observableを使用し、setChanged()およびnotifyObservers()を使用して、必要に応じてビューを更新します。

app.viewパッケージには、javax.swingコンポーネントを使用して表示を処理するクラス(またはさまざまな種類のディスプレイ用のいくつかのクラス)があります。これらのコンポーネントの一部は、モデルにフィードバックする必要があります。私が正しく理解していれば、ビューはフィードバックとは何の関係もないはずです。それはコントローラーによって処理されるべきです。

それで、私は実際にコントローラーに何を入れますか? Controllerのメソッドを呼び出すだけで、ビューにpublic void actionPerformed(ActionEvent e)を配置できますか?その場合、コントローラーで検証などを行う必要がありますか?もしそうなら、どのようにエラーメッセージをビューにフィードバックしますか?それがモデルを再び通過する必要がありますか、それともコントローラーがそれをビューに直接送信する必要がありますか?

ビューで検証が行われた場合、コントローラーには何を入れますか?

長い質問で申し訳ありませんが、プロセスについての私の理解を記録したかっただけで、誰かがこの問題を明確にしてくれることを願っています!

177
Paul Walker

あなたが提案した例では、あなたは正しいです:インターフェースの「ユーザーが「このアイテムを削除」ボタンをクリックした」は、基本的にコントローラーの「削除」機能を呼び出すだけです。ただし、コントローラーにはビューの外観がわからないため、ビューでは「どのアイテムがクリックされましたか?」などの情報を収集する必要があります。

会話形式で:

表示:「ねえ、コントローラー、ユーザーはアイテム4を削除したいと言っただけです。」
コントローラー:「うーん、資格情報を確認したので、彼はそれを行うことができます...ねえ、モデル、アイテム4を手に入れて、あなたがそれを削除するために何でもします」
モデル:「アイテム4 ...わかりました。削除されました。コントローラに戻ります。」
コントローラー:「ここで、新しいデータセットを収集します。戻って、表示します。」
表示:「クール、今すぐ新しいセットをユーザーに表示します。」

そのセクションの最後には、オプションがあります。ビューは、「最新のデータセットを提供する」という個別のリクエストを作成してより純粋にするか、「delete」で新しいデータセットを暗黙的に返します。 「操作。

487

MVCの問題は、ビュー、コントローラー、モデルが互いに可能な限り独立している必要があると人々が考えることです。ビューとコントローラーはしばしば絡み合っています-それをM(VC)と考えてください。

コントローラーは、ユーザーインターフェイスの入力メカニズムであり、特にGUIの場合、ビューに絡みつくことがよくあります。それでも、ビューは出力され、コントローラーは入力されます。ビューは多くの場合、対応するコントローラーがなくても機能しますが、通常、コントローラーはビューがなければ役に立ちません。ユーザーフレンドリーなコントローラーは、ビューを使用して、ユーザーの入力をより意味のある直感的な方法で解釈します。これは、コントローラの概念をビューから分離するのを難しくするものです。

モデルとして、密閉ボックス内の検出フィールドにある無線制御ロボットを考えてください。

モデルは、出力(表示)の概念や状態遷移のトリガーとなるものがない状態と状態遷移に関するものです。フィールド上のロボットの位置を取得でき、ロボットは位置を移行する方法を知っています(前進/後退/左/右に移動します。ビューまたはコントローラーなしで簡単に想像できますが、有用なことは何もありません)

コントローラーなしのビューを考えます。別の部屋のネットワーク上の別の部屋にいる誰かが、(x、y)座標がスクロールコンソールを流れ落ちるようにロボットの位置を監視しています。このビューはモデルの状態を表示するだけですが、この男にはコントローラーがありません。繰り返しますが、このビューはコントローラーなしで簡単に想像できます。

ビューのないコントローラーを考えてください。ロボットの周波数に調整された無線コントローラーで誰かがクローゼットに閉じ込められました。このコントローラーは入力を送信し、モデルに対して何をしているのかわからない状態遷移を引き起こします(ある場合)。想像するのは簡単ですが、ビューからのフィードバックのようなものがなければ本当に便利ではありません。

ほとんどのユーザーフレンドリーUIは、ビューをコントローラーと調整して、より直感的なユーザーインターフェイスを提供します。たとえば、ロボットの現在の位置を2次元で表示し、たまたまロボットの正面にある画面上のポイントにユーザーが触れることができるタッチスクリーン付きのビュー/コントローラーを想像してください。コントローラーには、ビューに関する詳細が必要です。ビューポートの位置とスケール、および画面上のロボットのピクセル位置に対してタッチされたスポットのピクセル位置)(無線コントローラーでクローゼットにロックされている男とは異なります)。

まだ質問に答えましたか? :-)

コントローラーは、モデルの状態を遷移させるために使用されるユーザーからの入力を受け取るものです。ビューとコントローラーを別々に保つようにしますが、それらは相互に依存していることが多いので、それらの境界があいまいである場合は問題ありません。つまり、ビューとコントローラーを別々のパッケージとして、きれいに分離できない場合がありますのような、しかしそれは大丈夫です。ビューがモデルからのものであるため、コントローラーがビューからきれいに分離されないことを受け入れる必要があるかもしれません。

...コントローラで検証などを行う必要がありますか?もしそうなら、どのようにエラーメッセージをビューにフィードバックしますか?それがモデルを再び通過する必要がありますか、それともコントローラーがそれをビューに直接送信する必要がありますか?

ビューで検証が行われた場合、コントローラーには何を入れますか?

リンクされたビューとコントローラーは、モデルを経由せずに自由に対話する必要があると言います。コントローラーはユーザーの入力を受け取り、検証を行う必要があります(おそらくモデルやビューの情報を使用します)が、検証が失敗した場合、コントローラーは関連するビューを直接更新できる必要があります(エラーメッセージなど)。

これに対する酸のテストは、他の誰かの検証エラーの結果として、独立したビュー(つまり、ネットワーク経由でロボットの位置を監視している他の部屋の男)が何かを見るべきかどうかです。ロボットにフィールドから降りるように指示しようとした)。一般的に、答えは「いいえ」です。検証エラーが状態遷移を妨げました。状態遷移がなかった(ロボットが移動しなかった)場合、他のビューに伝える必要はありません。クローゼットの男は、違法な遷移を引き起こそうとしたというフィードバックをまったく受け取らなかった(ビューがない-ユーザーインターフェイスが悪い)ので、他の誰もそれを知る必要はありません。

タッチスクリーンを持っている人がロボットをフィールドから送り出そうとした場合、彼は検出フィールドからロボットを送り出してロボットを殺さないように求めるニースのユーザーフレンドリーなメッセージを受け取りましたが、これを知っている必要はありません。

他のビューdoがこれらのエラーについて知る必要がある場合、ユーザーからの入力と結果のエラーはモデルの一部であり、全体がもう少し複雑な...

64
Bert F

MVCの基本に関する 良い記事 があります。

それは述べています...

コントローラー-コントローラーは、ビューとの対話をモデルによって実行されるアクションに変換します。

つまり、ビジネスロジックです。コントローラーは、ユーザーがビューで実行したアクションに応答して応答します。ここに検証を配置し、検証が失敗または成功した場合は適切なビューを選択します(エラーページ、メッセージボックスなど)。

別の良い点があります Fowlerの記事

23
JP Alioto

MVCパターンは単にあなたに プレゼンテーションを分離する (=ビュー) ビジネスロジックから (=モデル)。コントローラ部分は混乱を引き起こすためだけにあります。

16
Dimitri C.

あなたの質問に基づいて、私はあなたがモデルの役割について少しhaんでいるという印象を受けます。モデルは、アプリケーションに関連付けられたデータに固定されています。アプリにデータベースがある場合、モデルの仕事はそれと対話することです。また、そのデータに関連付けられている単純なロジックも処理します。 TABLE.foo == "Hooray!"であるすべての場合にそれを言うルールがある場合およびTABLE.bar == "Huzzah!"次にTABLE.field = "W00t!"を設定すると、モデルにそれを処理させることができます。

Controllerは、アプリケーションの動作の大部分を処理するものです。質問に答えるには:

「Controllerのメソッドを呼び出すだけで、Viewにpublic void actionPerformed(ActionEvent e)を配置できますか?」

私はノーと言います。私はそれがコントローラーに住んでいると言うでしょう。ビューは、ユーザーインターフェイスからコントローラーに送信されるデータを単純にフィードし、コントローラーが応答として呼び出すメソッドを決定するようにする必要があります。

「もしそうなら、検証などをコントローラーで行うべきですか?」

検証の大部分は、実際にコントローラーが行う必要があります。データが有効かどうかの質問に答える必要があり、有効でない場合は、適切なエラーメッセージをビューにフィードします。実際には、ユーザーエクスペリエンスを改善するために、いくつかの単純な健全性チェックをビューレイヤーに組み込むことができます。 (私は主にウェブ環境を考えています。ユーザーが「送信」を押すとエラーメッセージがポップアップするようにしたい場合があります。送信するまで待つのではなく、プロセス->ページサイクルをロードしてから、彼らが失敗したことを知らせる。)注意してください。あなたがしなければならない以上の努力を繰り返したくはありません、そして、多くの環境(再び、私はWebを考えています)で、あなたはしばしば不潔な不潔のパックとしてユーザーインターフェースから来るどんなデータも扱わなければなりませんあなたがそれが実際に合法であることを確認するまで嘘です。

「もしそうなら、エラーメッセージをビューにフィードバックするにはどうすればいいですか?それがモデルを再度通過する必要がありますか、それともコントローラーがそれをそのままビューに戻す必要がありますか?」

Controllerが通知するまで、Viewが次に何が起こるかを必ずしも知らないプロトコルをセットアップする必要があります。ユーザーがボタンを強打した後、どの画面を表示しますか?ビューは認識していない可能性があり、コントローラーは取得したデータを確認するまで認識しない可能性があります。 「予想どおり、この別の画面に移動する」または「この画面にとどまり、このエラーメッセージを表示する」などです。

私の経験では、モデルとビューの間の直接通信は非常に限定的であり、ビューはモデルのデータを直接変更するべきではありません。それがコントローラーの仕事です。

「検証がビューで行われた場合、コントローラーに何を入れますか?」

上記を参照;実際の検証はControllerで行う必要があります。そして、うまくいけば、今までに何をコントローラーに入れるべきかについてのアイデアを持っているでしょう。 :-)

エッジの周りが少しぼやける可能性があることに注意してください。ソフトウェアエンジニアリングと同じくらい複雑なもののほとんどと同様に、判断が求められます。最善の判断をして、このアプリ内で一貫性を保ち、学習したレッスンを次のプロジェクトに適用してみてください。

9
BlairHippo

実用的に言えば、コントローラーの概念が特に役立つものだとは思いませんでした。コードでは厳密なモデル/ビューの分離を使用していますが、明確に定義されたコントローラーはありません。それは不必要な抽象化のようです。

個人的には、本格的なMVCは、混乱を招き、複雑すぎる設計になりやすいという点で、工場設計パターンのようです。 アーキテクチャー宇宙飛行士 にしないでください。

9
John Kugelman

コントローラーは実際にはビューの一部です。その仕事は、要求を満たすためにどのサービスが必要かを把握し、ビューから値をサービスインターフェイスが必要とするオブジェクトに非整列化し、次のビューを決定し、応答を次のビューが使用できる形式に整列化することです。また、スローされた例外を処理し、ユーザーが理解できるビューにレンダリングします。

サービス層は、ユースケース、作業単位、モデルオブジェクトを知っているものです。コントローラーはビューのタイプごとに異なります-デスクトップ、ブラウザーベース、Flex、またはモバイルUIに同じコントローラーはありません。だから、私はそれが本当にUIの一部だと言います。

サービス指向:それは仕事が行われる場所です。

7
duffymo

コントローラーは、主にビューとモデルの間の調整を目的としています。

残念ながら、ビューと混ざってしまうことがあります-小さいアプリでは、それほど悪くはありません。

以下を配置することをお勧めします。

public void actionPerformed(ActionEvent e)

コントローラー内。次に、ビュー内のアクションリスナーがコントローラーに委任する必要があります。

検証部分については、ビューまたはコントローラーに配置できますが、個人的にはコントローラーに属していると思います。

パッシブビューとスーパーバイザープレゼンター(基本的にはモデルビュープレゼンターが少なくともFowlerによって分割されているもの)をご覧になることをお勧めします。見る:

http://www.martinfowler.com/eaaDev/PassiveScreen.html

http://www.martinfowler.com/eaaDev/SupervisingPresenter.html

3
Jon

ここに私が使用する経験則があります:thisページのアクションに具体的にを使用する手順の場合、それはコントローラーではなく、コントローラーに属します型。モデルは、データストレージへの一貫した抽象化のみを提供する必要があります。

MVCを理解していると思っていたが、実際には理解していなかった開発者によって書かれた大規模なWebアプリで作業した後、私はこれを思いつきました。それらの「コントローラー」は、通常はどこにも呼び出されない静的クラスメソッドの呼び出しの8行に削減されます:-/名前空間を作成する方法とほとんど同じようにモデルを作成します。これを適切にリファクタリングすると、3つのことが行われます。すべてのSQLをデータアクセスレイヤー(モデル)にシフトし、コントローラーコードをもう少し冗長にしますが、理解しやすくし、古い「モデル」ファイルをゼロにします。 :-)

3
staticsan

また、各Swingウィジェットは3つのMVCコンポーネントを含むと見なすことができることに注意してください。それぞれにModel(つまりButtonModel)、View(BasicButtonUI)、およびControl(JButton自体)があります。

1
akf

コントローラーに何を入れるかは基本的に正しいです。これは、モデルがビューと対話する唯一の方法です。実行されるアクションはビューに配置できますが、実際の機能はコントローラーとして機能する別のクラスに配置できます。これを行う場合は、コマンドパターンを調べることをお勧めします。これは、同じレシーバーを持つすべてのコマンドを抽象化する方法です。余談ごめんなさい。

とにかく、適切なMVC実装は次の相互作用のみを持ちます:モデル->ビューを表示->コントローラーコントローラー->表示

別の相互作用が発生する可能性がある唯一の場所は、オブザーバーを使用してビューを更新する場合、ビューはコントローラーに必要な情報を要求する必要があることです。

1
mnuzzo

このように、主にユーザー主導の入力/アクション(およびビュー、データ、明らかな_Modelを除くその他すべての_Logic)を処理し、それに対応するためにコントローラーを使用します。

(1)(応答、反応-webappがユーザーに応答して「行う」こと)Blog_Controller

-> main()

-> handleSubmit_AddNewCustomer()

-> verifyUser_HasProperAuth()

(2)(「ビジネス」ロジック、webappが「考える」ことと方法)Blog_Logic

-> sanityCheck_AddNewCustomer()

-> handleUsernameChange()

-> sendEmail_NotifyRequestedUpdate()

(3)(ビュー、ポータル、webappの「表示」方法)Blog_View

-> genWelcome()

-> genForm_AddNewBlogEntry()

-> genPage_DataEntryForm()

(4)(データオブジェクトのみ、_各ブログの(construct())で取得 *クラス、すべてのwebapp/inmemoryデータを1つのオブジェクトとしてまとめるために使用)Blog_Meta

(5)(基本データレイヤー、DBへの読み取り/書き込み)Blog_Model

-> saveDataToMemcache()

-> saveDataToMongo()

-> saveDataToSql()

-> loadData()

CまたはLのどこにメソッドを配置するかについて少し混乱することがあります。しかし、モデルは堅実で非常に透明であり、すべてのメモリ内データは_Metaにあるため、簡単です。 。私たちの最大の飛躍は、_Metaを採用することでした。これにより、さまざまな_C、_L、および_Modelオブジェクトからすべての不要物が取り除かれ、すべてが精神的に簡単に管理できるようになりました。 「依存性注入」と呼ばれる、またはすべてのデータとともに環境全体をやり取りする方法(ボーナスは「テスト」環境を簡単に作成できます)。

0
FYA

私が理解しているように、コントローラーはユーザーインターフェイスアクションからアプリケーションレベルのアクションに変換します。たとえば、ビデオゲームでは、コントローラーは「マウスを非常に多くのピクセルで動かした」を「そのような方向で見たい」と翻訳する場合があります。CRUDアプリでは、「そのようなボタンをクリックして」 「このことを印刷する」が、概念は同じです。

0
David Seiler