是或否:MVC中的模型是否包含应用程序逻辑?

Tim*_*imo 28 model-view-controller design-patterns

昨天我与我们的一位开发人员就MVC进行了一些讨论,更准确地说是模型组件在MVC中的作用.

在我看来,模型应该只包含属性而几乎没有功能,因此模型类中的方法尽可能少.

我的同事虽然认为模型可以而且应该具有更多功能并提供更多功能.

这是我们争论的一个例子.

例1

假设我们想创建一个博客.博客有文章和标签.每篇文章可以有多个标签,每个标签可以属于多个文章.所以我们在这里有关系.

在伪代码中它可能看起来像这样:

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

现在,假设我们正在松散耦合这里意味着我们可能会发生一个没有标签的文章实例,所以我们将使用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;
    }

}
Run Code Online (Sandbox Code Playgroud)

例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;
    }
}
Run Code Online (Sandbox Code Playgroud)

在模型之外你会做:blog.getArticles();或者article.getTags();在不打扰ajax调用的情况下获取标签.

然而,尽管这可能很方便,但我相信这种方法会破坏MVC,因为在一天结束时,所有模型都会充满各种各样的方法,控制器和助手类几乎什么都不做.

在我对MVC的理解中,模型应该只包含属性和内部最少的"辅助方法".例如,模型"Article"可以提供方法getNumOfTags(),但它不应该自己进行任何Ajax调用.

那么,哪种方法是正确的?

Dan*_*ady 27

一般来说,我也试图在逻辑方面保持控制器的简单性.如果需要业务逻辑,它将进入"服务层"类来处理它.这也节省了重复任何代码/逻辑,如果要改变业务逻辑,最终会使整个项目更易于维护.我只是将模型保持为实体对象.

我认为上面的答案很好地总结了它,很容易根据设计模式对项目进行过度设计:随身携带任何适用于您的工作并且最易于维护/高效.


ter*_*ško 12

你应该像某些类一样停止在MVC中处理"模型".模型不是类或对象.模型是一个层(在现代MVC中,自概念开始以来已经有了一些演变).人们倾向于称之为"模型"的实际上是域对象(我将Rails归咎于这种大众愚蠢).

应用程序逻辑(域逻辑结构和存储抽象之间的交互)应该是模型层的一部分.更确切地说:它应该在里面Services.

表示层(模型,视图,布局,模板)和模型层之间的交互应该仅通过这些服务进行.

应用程序在控制器中没有位置.控制器是表示层的结构,它们负责处理用户输入.请不要向其公开deomain对象.

  • 责备的Rails事情使我发笑。 (3认同)

Ale*_*r R 6

正确?无论是.他们都编译,不是吗?

方便的技巧很好,为什么不使用它们你可以吗?话虽这么说,正如你所指出的,如果你把各种逻辑放在其中,你可能会得到臃肿的模型.同样地,当你在每个动作中执行大量操作时,你可以得到臃肿的控制器.有两种方法可以抽象出任何元素,如果这也是必要的话.

在一天结束时,所有设计模式都是指导方针.你不应该盲目地遵循任何规则,只因为别人说了.做什么对你有用,你认为提供干净,可扩展的代码,并达到认为制作好代码的任何指标.

总而言之,对于真正的理想主义MVC,我会说模型不应该有任何外部动作,它们只是数据表示,仅此而已.但随意不同意:-)

  • +1"在一天结束时所有的设计模式都是指导方针".如果你担心一个臃肿的模型,提取对DAO的数据访问 - 根据定义,数据访问对象,它们既不是模型也不是控制器. (2认同)

Min*_*hev 6

您对模块的建议(内部没有任何业务逻辑)听起来更像是在谈论Value Objects.你大学的建议听起来更像是Domain Objects.

在我看来,将要使用的概念取决于所使用的框架(这是实践观点,更具哲学性的观点).如果使用框架,它通常会设置有关如何实现每个组件的规则.例如,我们可以查看不同的MVC框架.在Flex的Cairngorm框架中,我们都有.VO(值对象)主要用于绑定视图,而DO(域对象)保存业务逻辑.如果我们看看ASP.NET的MVC实现,我们有一个模型,它至少包含数据(VO),但也包含一些验证(如果需要).让我们看一下UI MV*框架 - 例如Backbone.js.Backbone的文档说:

模型是任何JavaScript应用程序的核心,包含交互式数据以及围绕它的大部分逻辑:转换,验证,计算属性和访问控制.

如果我们看一下Smalltalk提供的传统MVC,我们会看到:"模型:管理应用程序域的行为和数据",因此我们在其中有一些行为,而不仅仅是普通数据.

让我们想一想,如果模型中没有任何逻辑,我们应该将所有应用程序和业务逻辑放入Controller中.

现在让我们关注一个具体的例子.想象一下,我们有一个图表模型.我们想要找到它中两个节点之间的最短路径.一个好问题是在哪里找到找到最短路径的算法?这是一种商业逻辑,对吧?如果我们看一下MVC(代码重用,DRY等)的主要好处,我们可以看到,如果我们想以最佳方式重用我们的模型,我们应该在其中实现最短路径.最短路径算法通常依赖于图形内部表示(或者至少为了算法的最佳性能),但是这种表示被封装到模型中,遗憾的是我们不能重用矩阵表示的完整最短路径和邻居列表,所以它不是将它放入控制器的好主意.

因此,作为结论,我可以说这取决于您的需求(主要是).传统的MVC目的是在UI中使用(在GoF内部)

模型/视图/控制器(MVC)三元组[首先由Krasner和Pope在1988年描述]用于在Smalltalk-80中构建用户界面.

)

现在我们在不同的领域使用它 - 只有UI,用于Web应用程序等.因此,它不能以纯粹的形式使用它.但无论如何,在我看来,关注点的最佳分离可以通过将业务逻辑隔离到模型中,将应用程序逻辑隔离到Controller中来实现.