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


186

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

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

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

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

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

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

長い質問でごめんなさい、私はプロセスの私の理解を文書化したかったのですが、うまくいけば誰かがこの問題を私のために明確にしてくれるでしょう!

回答:


520

あなたが提案した例では、あなたは正しいです:インターフェースの「ユーザーが「このアイテムを削除」ボタンをクリックした」は、基本的にコントローラーの「削除」関数を呼び出すだけです。ただし、コントローラーはビューがどのように表示されるかを認識していないため、ビューでは「クリックされた項目は何か」などの情報を収集する必要があります。

会話フォームで:

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

そのセクションの最後には、オプションがあります。ビューは、「最新のデータセットを取得する」という別のリクエストを作成して、より純粋にすることができます。または、コントローラーが暗黙的に「削除"操作。


90
その対話は私が遭遇したMVCの最も良い説明です、ありがとう!
ポールウォーカー

13
すべて問題ありませんが、モデルから直接ビューを読み取っても問題はありません。「コントローラはデータポリスではありません」。コントローラを薄く保つように言う教義もあります。ビューヘルパーは、ビューで使用する準備ができているデータを収集するのに最適な場所です。一部のデータアクセスロジックを再利用するために、完全なコントローラスタックをディスパッチする必要はありません。詳細:rmauger.co.uk/2009/03/…–
例外e

1
「例外e」に同意します。モデル内のデータは、コントローラーではなく多くのイベントによって更新できるため、一部のMVC設計では、MはVにデータがダーティであり、Vが自身を更新できることをVに通知します。その場合、Cが果たす役割はありません。
Mishax 2014年

68

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

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

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

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

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

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

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

私はもうあなたの質問に答えましたか?:-)

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

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

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

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

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

タッチスクリーンを持った男がロボットをフィールドから送り出そうとした場合、ロボットを検出フィールドから送り出してロボットを殺さないようにとのメッセージが表示されましたが、他の誰もこれを知る必要はありません。

他のビューこれらのエラーについて知る必要がある場合、ユーザーからの入力と結果として生じるエラーはモデルの一部であり、全体が少し複雑であることを効果的に言っています...


23

ここで良い記事 MVCの基礎には。

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

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

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

Fowlerには別の良い記事があります。


MVPは、参照する記事で説明されている別のオプションです。martinfowler.com/ eaaDev / ModelViewPresenter.html
Jon

リンクをありがとう、彼らは確かに興味深い読書をします。
ポールウォーカー

18

MVCパターンでは、プレゼンテーション(=ビュー)をビジネスロジック(=モデル)から分離するだけです。コントローラー部分は混乱を招くだけです。


1
まさに、今までずっと思っていたのですが、誰にも言う勇気がありませんでした…。
user1451111

1
モデルビューの混乱

10

実際には、コントローラーの概念が特に便利なものであることに気づきませんでした。私のコードでは厳密なモデル/ビューの分離を使用していますが、明確に定義されたコントローラーはありません。それは不必要な抽象化のようです。

個人的には、本格的なMVCは工場の設計パターンのように見え、混乱を招きやすく、複雑すぎる設計になりがちです。しないでください建築の宇宙飛行士に


9

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

コントローラは、アプリケーションの動作の大部分を処理するものです。だからあなたの質問に答えるために:

コントローラーのメソッドを呼び出すだけで、ビューにpublic void actionPerformed(ActionEvent e)を配置できますか?

私はノーだと思います。私はそれがコントローラーに存在するべきだと思います。ビューは、ユーザーインターフェイスからのデータをコントローラーにフィードし、コントローラーが応答として呼び出すメソッドを決定するようにします。

その場合、コントローラーで検証などを行う必要がありますか?

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

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

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

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

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

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

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


7

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

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

サービス指向:ここで作業が行われます。


3

コントローラは、主にビューとモデル間の調整用です。

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

私はあなたが置くことをお勧めします:

public void actionPerformed(ActionEvent e)

コントローラで。次に、ビューのアクションリスナーがコントローラーに委任されます。

検証部分はビューやコントローラーに入れてもいいですが、個人的にはコントローラーに属していると思います。

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

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

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


3

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

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


1

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


1

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

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

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


0

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


0

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

(1)(応答、反応-Webアプリケーションがユーザーに応じて「実行」すること)Blog_Controller

-> main()

-> handleSubmit_AddNewCustomer()

-> verifyUser_HasProperAuth()

(2)(「ビジネス」ロジック、Webアプリケーションが「考える」ものと方法)Blog_Logic

-> sanityCheck_AddNewCustomer()

-> handleUsernameChange()

-> sendEmail_NotifyRequestedUpdate()

(3)(ビュー、ポータル、Webアプリケーションの「表示」方法)Blog_View

-> genWelcome()

-> genForm_AddNewBlogEntry()

-> genPage_DataEntryForm()

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

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

-> saveDataToMemcache()

-> saveDataToMongo()

-> saveDataToSql()

-> loadData()

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

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.