为什么每个人都将控制器放在一个文件夹中而将视图放在另一个文件夹中?


36

我正准备把弯腰的问题从asp转移到mvc框架(asp.net mvc或nancy)中。无论我走到哪里,都可以看到用于控制器/模块的文件夹和用于视图的文件夹。这是只是按类型整理事物的帕夫洛夫式反射,还是有一些更深层次的智慧在起作用?我有一个概念验证项目,将可能要一起打开的文件存储在一起,这很舒适。由于这些文件也可能互相调用,因此可以使用较短,较不易碎的相对链接进行调用。这种模式受到mvc的挑战,因为文件夹路径不再自动对应于url路径,并且在asp.net mvc中,项目模板和路由强制执行views \ controllers \ schism。

microsoft页面介绍了区域的概念。可以理解为,由于这种人为的分离,大型应用程序变得笨拙。

人们会反对“关注点分离”,但是关注点分离已经通过拥有单独的源文件来实现。在我看来,将这些紧密耦合的源文件发送到文件夹结构的相对两端并没有任何具体收获?

还有其他人要与这个斗争吗?有小费吗?


3
您认为将后端代码文件与视图文件分开是不合逻辑的吗?如果我们已经确定了方向,为什么不将相关的CSS和JavaScript文件也放在同一文件夹中?
Alternatex

2
如果您获得了Resharper,则View在控制器中调用F12会将您带到视图,而在视图的右键单击菜单中的第一个选项会将您带到控制器,并且缺少导航的整个问题都消失了。
皮特·柯坎

4
@Alternatex对不起,但我看不到控制器是如何“后端”的。它与视图紧密相关。没有控制器,视图就没有好处,没有视图,控制器就没有用。有一天,您将希望将其一起删除。对我而言,这是对文件夹中所有内容的最佳测试?除非有人可以向我展示更好的方法?
bbsimonbb '16

2
视图是表示层。控制器是包含服务的层,服务可能包含您域中的对象,其中包含您的业务逻辑,因此包含您的应用程序的后端逻辑。视图仅包含琐碎的条件和简单的循环,用于遍历集合,如果视图包含其他内容,则说明您做错了。与常规结构一样,您将后端和前端分开了,对我来说,这比您建议的结构更好。
安迪

10
因此,不乏人告诉我控制器是陶器,因此它进入了陶器橱柜。视图是玻璃,因此它们可以进入玻璃橱柜。我知道控制器很简单,但是我建议最好有一个午餐柜和一个晚餐柜,因为我们谈论的是仅在午餐晚餐时使用的东西。
bbsimonbb '16

Answers:


38

我想说的是货物崇拜编程,但是这种结构有技术原因。Asp.Net MVC几乎对所有方法都采用了配置方法上的约定。默认情况下,Razor视图引擎搜索Views目录,以便解析要从控制器返回的视图。但是,有一些技巧可以使项目结构变得不同,Microsoft甚至提供了称为Areas的MVC功能,使我们可以创建更合理的项目结构。您也可以实现自己的视图引擎,以指定在何处查找视图。

为什么我说这是对货物的狂热编程,您对此是否正确?Bob叔叔说服我,项目的目录结构不应该告诉我这是MVC应用程序。它应该告诉我这是一个商店前台,一个休假请求系统等等。高层次的结构和体系结构应该告诉我们这个东西是什么,而不是如何实现的。

简而言之,我相信您对此是正确的,但是当我说您不想尝试使Asp.Net MVC框架执行原本应该做的事情时,任何其他目录结构都将与该框架抗争并信任我不打算这样做。可惜的是它确实不是可配置的。


为了快速解决架构问题,我仍然相信业务模型(业务而非视图)和DAL应该位于一个单独的项目/库中,该项目/库可从您的MVC应用程序调用。

只是控制器确实与视图紧密结合,并可能一起修改。记住依赖关系耦合和逻辑耦合之间的区别是我们明智的选择。仅仅因为代码的依赖关系已解耦,并不能减少其逻辑耦合。


1
控制其中剃须刀的外观的意见的综合治理是在这里。我可能会坚持下去。
bbsimonbb '16

3
我按照建议在x1.25观看了bob叔叔。这让我想到,如果IT建筑师建造建筑物,我们所有人都将生活在成群结队的小筏上,漂浮在某个湖上。生活不一定会更简单。
bbsimonbb '16

1
它有点复杂。要真正定位控制器和视图的位置,您将需要在运行时解析视图路径,以包括控制器名称空间。这是这样做的方法
bbsimonbb '16

1
还要看看面向功能的软件开发(fosd.net),以了解Bob叔叔所描述的学术知识。
ngm

1
康韦定律在起作用。我敢肯定,这对您的公司@Flater有用。标准的MVC布局可在许多公司使用,但是我仍然更喜欢按语义而不是语法对事物进行分组。我所工作的公司是围绕产品组织的,而不是角色/职务。我相信这是我偏爱这里的根源。
RubberDuck

9

不管是什么原因,这都是不好的做法。这是非常防OO的方法,因为程序包或文件夹(无论如何称呼它们)都应具有弱的相互依赖性。其中的类(或文件)应具有很强的相互依赖性。

通过将所有视图放在一个文件夹中,并将所有控制器放在另一个文件夹中,您将创建两个具有非常紧密耦合的包。这违反了具有弱的包间依赖关系的原理。

视图和控制器是一个整体的两半,应该属于彼此。您不会为左边的袜子画一个橱柜,而为右边的袜子画一个橱柜。


6

回答您的“为什么所有人...?” 问题:以下是一些潜在原因,尽管我不确定是哪种组合是真正的原因,因为这实际上是一个主观问题

  • 用匹配的文件夹和名称空间结构复制逻辑体系结构(模型,视图,控制器)

  • 违反常规且不方便遵循ASP.net MVC项目模板

  • 要按名称空间分组,因为Controllers/文件夹将导致一个.Controllers名称空间

  • 可能会启用DI / IoC中的某些方案,其中仅从包含/以“ Controllers”结尾的名称空间中查询/扫描控制器类(这可能是错误的)

  • 允许T4模板扫描并搭建模型和控制器以生成视图

如果对您的项目有意义,那么您始终可以创建并遵循自己的约定,没有人能够/会阻止您。但是请注意,如果您在大型项目和/或大型团队中工作,那么每个人都知道的默认约定可能是一个更好的选择(不过不一定是正确的约定!)

如果您的约定更容易遵循且不会影响生产率,那么一定要这样做!甚至在博客上写一两篇文章,与开发者社区进行社交并获得反馈


2

将视图和控制器保留在单独目录中的一个原因是,当前端和后端开发人员在一个项目上工作时。您可以阻止前端开发人员访问后端代码(例如,帮助实现PCI合规性,限制谁可以访问敏感代码)。

另一个原因是,通过对视图路径进行较小的更改,可以更轻松地拥有“主题”并切换出所有模板。

第三个原因是在MVC框架中指定视图时具有简单的目录模式。指定子目录和文件比访问每个视图的路径大而容易。

唯一的“紧密耦合”应为:

  1. 控制器将变量发送到视图中。
  2. 视图中的表单字段或动作,发送回控制器。

我使用通用控制器,并尝试将变量名称保留在通用视图中,以便许多视图可以使用同一控制器,并且许多控制器可以使用同一视图。因此,我倾向于将视图完全分开。在模型中,您可以区分应用程序中的每个“事物”,它们可以是具有一系列属性的对象,以及可以访问/修改这些属性的方法。

对于紧密耦合的代码,一种可能对您有用的方法是将作为包或“模块”一部分的所有文件放在命名空间目录中。然后,您可以使用脚本将原始模板复制或“编译”到主“ views”目录中。例如:


    lib/my-namespace/my-package/
        -> controllers/
        -> models/
        -> views/
            -> theme/
               -> template-name1
    app/compiled_views/theme/
        -> url/path/template-name1

不幸的是,如果您想更改现有主题的结构,则需要在包目录中进行更多的编织,以更新视图。

考虑到视图只是格式化数据的一种方法,无论是json,xml,csv还是html。如果您希望您的应用程序也可以用作API,则这特别有帮助。尝试通过使用通用变量名称将视图与数据分离,以便可以将相同的模板用于许多控制器或模型(或使用include来最大程度地减少需要维护的代码量)。


1

不一定每个人都这样做。例如,python的Django框架具​​有应用程序的概念,其中应用程序的子模块位于具有自己的模型,视图和模板的目录中(视图实质上是Django称为控制器)。我碰巧更喜欢这种做事方式,因为这意味着我可以轻松地打包“应用程序”并在项目中重复使用,只需将其包含在项目设置的应用程序列表中即可。查找不同零件的位置也更容易。如果我查看urls.py文件并看到类似的内容url(r'^users/', include('my_site.users.urls')),我知道该模块my_site.users包含了处理用户的所有代码。我知道要查看模块my_site.users.views以及my_site.users.models何时要查看如何创建和认证用户。我知道我的所有路线都在中定义my_site.users.url

同样,如果通用性足够强,我可能可以通过更改配置或将其打包为库并将其发布为OSS来在其他站点中使用该模块。


1

请记住,这是Microsoft建议的将控制器和视图保存在不同文件夹中的方法,因此很多人会遵循推荐的结构,

  1. 原因之一可能是控制器始终与视图没有一对一的关系,尤其是部分视图可以在控制器之间共享。
  2. 另一个原因可能是您的项目增长时,您可能希望将控制器和单元测试拉到另一个项目,但是对于视图加上view / js / css相互引用在一起是非常困难的。

话虽如此,有很多关于按自己的方式进行操作的文章,例如this


“这是Microsoft推荐的方式” ...您能否阐明您的意思?是否有关于此的实际,权威的MS文章?或者,这仅仅是MVC应用程序的默认项目设置吗?并且,如果您基于后者,那么默认的MVC项目设置是否可能是因为它是“每个人”的工作方式呢?
svidgen '16

1
根据文章msdn.microsoft.com/en-us/library/...它说:“控制器,这是推荐”等的意见,等等。
低空飞行的鹈鹕

0

作为记录

为什么我说这是对货物的狂热编程,您对此是否正确?Bob叔叔说服我,项目的目录结构不应该告诉我这是MVC应用程序。它应该告诉我这是一个商店前台,一个休假请求系统等等。高层次的结构和体系结构应该告诉我们这个东西是什么,而不是如何实现的。

问题:谁有权访问该代码?程序员。最终用户是否关心代码?不,而且程序员要做的是编写代码。更具体地说,是基于类型的类(控制器,服务,模型等)。因此,如果您能够根据代码类型而不是根据代码的行为查找代码,那么这很有意义并且调试代码很容易。另外,假设有一个团队项目,一个负责控制器,另一个负责模型,另一个负责视图和视图。将项目分成几部分很容易。好的代码是易于调试的代码,而不是语法糖代码。鲍伯叔叔又错了。

试图模仿项目的行为(店面)是一件大事。


3
我最关心的是功能,而不是类型。当我看到某个功能无法正常工作时,我知道与该功能相关的代码有问题,而我不一定知道它是哪种类型的代码。
停止伤害莫妮卡

1
“比方说,一个团队项目,一个负责控制器,另一个负责模型,另一个负责”。这样的团队将很难运送任何东西,而当他们这样做时,将花费更多的通信开销和错误。
RubberDuck

正如在已接受的答案下的评论链中所确定的那样,您只有在某些情况下才有意思。当一家公司专注于向许多不同客户出售的MVC项目时,保持MVC结构对于可重用性很有意义。当一家公司专注于利基市场(例如网上商店)并且可能使用许多不同的技术时,具有面向网上商店的结构就更有意义了。这是康韦定律的实际应用。代码(以及项目结构)应遵循公司的结构。
平坦的

@RubberDuck:您可以使用任何一种方法来增加成本。没错,当不同的人执行不同的技术组件时,您可以增加业务逻辑通信。但是,如果您有不同的人完全实现不同的功能,则可能会增加成本,以确保每个人都使用相同的技术方法(熟练+同意)。无论哪种方式,您都需要通信开销来确保人们一起工作。
平坦

是的,交接总是比一对开发人员实施端到端IME功能要花费更多。
RubberDuck
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.