web-dev-qa-db-ja.com

はいまたはいいえ:MVCのモデルにアプリケーションロジックを含める必要がありますか?

昨日、MVCについて開発者の1人と、より正確にはMVCでのモデルコンポーネントの役割についていくつか話し合いました。

私の意見では、モデルにはプロパティのみが含まれ、機能はほとんど含まれていないため、モデルクラスのメソッドは可能な限り少なくなります。

しかし私の同僚は、モデルにはそれ以上の機能があり、さらに多くの機能を提供できると考えています。

これは私たちが議論した例です。

例1

ブログを作成したいとしましょう。ブログには記事とタグがあります。各記事は複数のタグを持つことができ、各タグは複数の記事に属することができます。ここにm:nの関係があります。

疑似コードでは、おそらく次のようになります。

_class Article{
    public int id;
    public String title;
    public String content;
    public Tag[] tags;

    // Constructor
    public void Article(id, title, content, tags){
        this.id = id;
        this.title = title;
        this.content = content;
        this.tags = tags;
    }
}

class Tag{
    public int id;
    public String name;

    // Constructor
    public Tag(id, name){
        this.id = id;
        this.name = name;
    }
}
_

ここで、ここで疎結合で作業していると仮定します。つまり、まだタグのないArticleのインスタンスがある可能性があるため、Ajax呼び出しを使用します(すべての情報を含むデータベースを持つバックエンドに対して)。私たちの記事に属するタグを取得します。

ここでトリッキーな部分があります。 Ajax + JSONを介してバックエンドデータを取得することは、パーサーを使用してajaxリクエストを処理する専用クラスを使用するコントローラーの仕事であると私は信じています。

_class MyController{
    private void whatever(articleID){
        Article article = (Article) ContentParser.get(articleID, ContentType.ARTICLE);
        doSomethingWith(article);
    }
}

public abstract class ContentParser{
    public static Object get(int id, ContentType type){
        String json = AjaxUtil.getContent(id, type.toString()); // Asks the backend to get the article via JSON
        Article article = json2Article(json);

        // Just in case
        Tag[] tags = article.tags;
        if (tags == null || tags.length <= 0){
            json = AjaxUtil.getContent(article.id, ContentType.TAGS); // Gets all tags for this article from backend via ajax
            tags = json2Tags(json);
            article.tags = tags;
        }

        return article;
    }

    // Does funky magic and parses the JSON string. Then creates a new instance of Article
    public static Article json2Article(String json){
        /*
         ...
        */
        return new Article(id, title, content, tags);
    }

    // Does funky magic and parses the JSON string. Then creates a new instance of Tag
    public static Tag[] json2Tags(String json){
        /*
         ...
        */
        return tags;
    }

}
_

例2

私の同僚は、これはMVCの考え方に反すると信じており、モデルはこれに注意する必要があると示唆しています。

_class Blog{
    public int id;
    public String title;
    public Article[] articles;

    // Constructor
    public Blog(id, title, articles){
        this.id = id;
        this.title = title;
        this.articles = articles;
    }

    public void getArticles(){
        if (articles == null || articles.length <= 0){
            String json = AjaxUtil.getContent(id, ContentType.ARTICLE); // Gets all articles for this blog from backend via ajax
            articles = json2Articles(json);
        }
        return articles;
    }

    private Article[] json2Articles(String json){
        /*
         ...
        */
        return articles;
    }

}

class Article{
    public int id;
    public String title;
    public String content;
    public Tag[] tags;

    // Constructor
    public Article(id, title, content, tags){
        this.title = title;
        this.content = content;
        this.tags = tags;
    }

    public Tag[] getTags(){
        if (tags == null || tags.length <= 0){
            String json = AjaxUtil.getContent(id, ContentType.TAGS); // Gets all tags for this article from backend via ajax
            tags = json2Tags;
        }
        return tags;
    }

    // Does funky magic and parses the JSON string. Then creates a new instance of Tag
    private Tag[] json2Tags(String json){
        /*
         ...
        */
        return tags;
    }
}
_

また、モデルの外では、blog.getArticles();またはarticle.getTags();を使用して、ajax呼び出しに煩わされることなくタグを取得できます。

しかし、これは便利かもしれませんが、MVCではこのアプローチはうまくいかないと思います。結局のところ、すべてのモデルはさまざまなファンキーなことを行うメソッドでいっぱいになり、コントローラーとヘルパークラスはほとんど何もしません。

MVCについての私の理解では、モデルにはプロパティと最小限の「ヘルパーメソッド」のみを含める必要があります。たとえば、モデル「Article」はメソッドgetNumOfTags()を提供できますが、それ自体ではAjax呼び出しを行うべきではありません。

では、どちらのアプローチが正しいのでしょうか?

30
Timo

一般的に、ロジックの観点からもコントローラーをシンプルに保つようにしています。ビジネスロジックが必要な場合は、「サービスレイヤー」クラスで処理します。これにより、コードやロジックを繰り返す必要もなくなります。これにより、ビジネスロジックが変更された場合に、最終的にプロジェクト全体の保守が容易になります。私はモデルを純粋にエンティティオブジェクトとして保持しています。

上記の答えはそれをうまく要約していると思いますが、設計パターンに基づいてプロジェクトを過剰に設計することは簡単です。

26
Danny Brady

MVCで「モデル」をクラスのように扱うのはやめるべきです。モデルはクラスまたはオブジェクトではありません。モデルはレイヤーです(現代のMVCでは、コンセプトの開始以来、いくつかの進化がありました)。人々が「モデル」と呼ぶ傾向があるのは、実際には domain object です(Railsこの質量の愚かさのせいです)。

アプリケーションロジック(ドメインロジック構造とストレージ抽象化の間の相互作用)は、モデルレイヤーの一部である必要があります。より正確には、Services内にある必要があります。

プレゼンテーションレイヤー(モデル、ビュー、レイアウト、テンプレート)とモデルレイヤーの間の相互作用は、これらのサービスを通じてのみ発生します。

アプリケーションはコントローラーに場所がありません。コントローラーはプレゼンテーション層の構造であり、ユーザー入力の処理を担当します。 Deomainオブジェクトを公開しないでください。

10
tereško

正しい?どちらか。両方ともコンパイルしますね。

便利なトリックはいいですが、それを使用してみませんか?そうは言っても、指摘したように、あらゆる種類のロジックをモデルに組み込むと、モデルが肥大化する可能性があります。同様に、ただし、各アクションで大量のコントローラーを実行すると、コントローラーが肥大化する可能性があります。必要に応じて、どちらかから要素を抽象化する方法もあります。

結局のところ、すべての設計パターンはガイドラインです。他の誰かが言ったからといって、ルールを盲目的に守るべきではありません。あなたのために機能することを実行してください、あなたが考えるものはクリーンで拡張可能なコードを与え、どんなメトリックスにもヒットしますあなた良いコードを作ると思います。

とはいえ、真の理想主義的なMVCの場合、モデルには外部アクションを含めるべきではなく、モデルはデータ表現であり、それ以上のものではありません。しかし、反対することを遠慮なくしてください:-)

6
Alexander R

モジュール(内部にビジネスロジックなし)に関する提案は、値オブジェクトについて話しているように聞こえます。あなたの大学の提案は、ドメインオブジェクトのように聞こえます。

私の意見では、使用される概念は、使用されるフレームワークに依存します(それが実際的な見方ですが、より哲学的なものは以下のとおりです)。フレームワークを使用する場合は、通常、各コンポーネントの実装方法に関するルールを設定します。たとえば、さまざまなMVCフレームワークを見ることができます。 Flexの Cairngorm フレームワークには両方あります。 VO(値オブジェクト)は主にビューへのバインドに使用され、DO(ドメインオブジェクト)はビジネスロジックを保持します。 ASP.NETのMVC実装を見ると、少なくともデータ(VO)だけでなく、検証(必要な場合)も含むモデルがあります。 UI MV *フレームワークを見てみましょう-たとえば、Backbone.js。バックボーンのドキュメントは言う:

モデルはJavaScriptアプリケーションの中心であり、インタラクティブデータと、それを取り巻くロジックの大部分(変換、検証、計算されたプロパティ、アクセス制御)が含まれています。

Smalltalk によって提供される従来のMVCを調べると、「モデル:アプリケーションドメインの動作とデータを管理する」ことがわかります。そのため、単純なデータだけでなく、いくつかの動作があります。

実際に考えてみましょう。モデルにロジックがない場合は、すべてのアプリケーションとビジネスロジックをコントローラーに配置する必要があります。

次に、具体的な例に焦点を当てましょう。グラフであるモデルがあると想像してください。その中の2つのノード間の最短経路を見つけたいと思います。良い質問は、最短経路を見つけるアルゴリズムをどこに置くかです。一種のビジネスロジックですね。 MVCの主な利点(コードの再利用、DRYなど))を見ると、モデルを可能な限り最適な方法で再利用したい場合は、内部に最短パスを実装する必要があります。通常、最短パスアルゴリズムはグラフの内部表現に依存します(または少なくともアルゴリズムの最高のパフォーマンスを得るには)。この表現はモデルにカプセル化されます。残念ながら、マトリックス表現と近隣のリストに完全な最短パスを再利用できないためそれをコントローラーに入れるのは良い考えです。

結論として、私はそれはあなたのニーズに依存する(ほとんど)と言えるでしょう。従来のMVCの目的は、UI(GoF内)で使用することです

クラスのモデル/ビュー/コントローラー(MVC)トライアド[1988年にクラスナーとポープによって最初に説明された]は、Smalltalk-80でユーザーインターフェイスを構築するために使用されます。

今ではさまざまな領域で使用しています-WebアプリケーションなどのUIのみです。そのため、純粋な形式では使用できません。しかし、とにかく、私の意見では、ビジネスロジックをモデルに分離し、アプリケーションロジックをコントローラーに分離することで、懸念事項の最良の分離を実現できます。

6
Minko Gechev

つまり、モデルはビューに送信されるデータにすぎないと考えています。 MVCパラダイムをアプリケーションの他の側面に組み込むのに役立ちます。

MVCパターンを壊さないようにする場合は、データをすべてビジネスモデルとしてコントローラーに返し、ViewModelにアンパックする必要があります。情報サーバー側に要求し、すべてを送信します。 JSonリクエストを行う必要がある場合、それはRestサービスかコントローラの呼び出しのいずれかである必要があります。これらのgetTagsとgetArticlesを使用すると、非常に面倒になります。ビューで呼び出し先を決定している場合...その情報を事前に入手できない理由を理解できません。静的メソッドの使用は、同じアプローチであり、角度が異なります。

私のコントローラーアクションが、マジックを実行する注入されたサービスを呼び出し、MVC Webアプリケーション内のモデルを使用して情報を返すのが最適であることがわかりました。これにより、事柄がよりわかりやすくなり、関心の分離がさらに強調されます。その後、コントローラーアクションは非常にリーンになり、何をしているのかが明確になります。

モデルを完全にダムとして扱うことから始めることは、このコードから私が見ているこれらのアーキテクチャ上の問題のいくつかを分類するのに長い道のりを行くかもしれないと思います。

4
Jonathan