35

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

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

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

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

例1

ブログを作成したいとしましょう。ブログには記事とタグがあります。各記事には複数のタグを付けることができ、各タグは複数の記事に属することができます。したがって、ここにはam: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呼び出しを実行するべきではありません。

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

4

6 に答える 6

30

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

上記の答えはうまくまとめられていると思いますが、デザインパターンに基づいてプロジェクトをオーバーエンジニアリングするのは簡単です。自分に合ったものを選び、最も保守性が高く効率的です。

于 2012-12-19T12:07:44.680 に答える
16

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

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

プレゼンテーション層(モデル、ビュー、レイアウト、テンプレート)とモデル層の間の相互作用は、これらのサービスを介してのみ発生する必要があります。

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

于 2012-12-19T19:20:01.320 に答える
7

正しい?また。どちらもコンパイルしますね。

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

結局のところ、すべてのデザインパターンはガイドラインです。他の誰かが言ったからといって、盲目的に規則に従うべきではありません。自分に合った方法を実行し、クリーンで拡張可能なコードを提供し、適切なコードを作成すると思われるメトリックにヒットすると思うことを実行します。

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

于 2012-12-19T11:46:05.430 に答える
7

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

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

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

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

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

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

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

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

)。

現在、さまざまな分野で使用しています。UIのみ、Webアプリケーションなどです。そのため、純粋な形式で使用することはできません。しかし、とにかく、私の意見では、関心の分離は、ビジネスロジックをモデルに分離し、アプリケーションロジックをコントローラーに分離することで達成できます。

于 2012-12-19T12:08:17.450 に答える
4

つまり、モデルはビューに送信されるデータである必要があると思います。これは、MVCパラダイムをアプリケーションの他の側面に推進するのに役立ちます。

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

コントローラーアクションに、魔法を実行する注入されたサービスを呼び出し、MVCWebアプリケーション内のモデルを使用して情報を返すのが最善であることがわかりました。これは物事をよりきれいにし、懸念の分離をさらに強調します。その後、コントローラーアクションは非常にスリムになり、何をしているのかが明確になります。

モデルを完全に馬鹿げたものとして扱うことから始めることは、私がこのコードから見ているこれらのアーキテクチャ上の問題のいくつかを分類するのに大いに役立つかもしれないと私は信じています。

于 2012-12-19T12:10:13.323 に答える
2

はい。そうすべき。あなたはドメイン駆動設計について話している。

https://en.wikipedia.org/wiki/Domain-driven_design

あなたが感じるなら、あなたはそれをしていません、そしてあなたは貧血ドメインモデル設計をしています。それはアンチパターンです。

貧血領域の設計がいかに悪いかについて、MartinFlowerの記事を読みました。https://martinfowler.com/bliki/AnemicDomainModel.html

于 2019-11-14T17:21:37.767 に答える