首页 > 动态语言, 挨踢(IT) > 《Agile Web Development with Rails》抄书笔记(03):Rails架构简介

《Agile Web Development with Rails》抄书笔记(03):Rails架构简介

2013年4月8日 发表评论 阅读评论 963 人阅读    

《Agile Web Development with Rails》抄书笔记系列

  “《Agile Web Development with Rails》抄书笔记系列”目录

  经过上面两节的介绍和折腾,我们对Rails有了一个初步的认识。这一节,我们抛开具体的编码,站在更高的层次去俯瞰一下整个Rails程序的架构。

  对于Rails来说,一个很诱人的特性就是Web程序的组织结构。这种结构可以让人更加容易地去创建应用程序。但是这是为什么呢?这一节我们就来看看这其中的奥妙。

Model、View与Controller

  让我们暂时将时光返回到1979年,Trygve Reenskaug为交互程序设计了一种新的架构。在他的设计中,应用程序被划分成了三个组件中去:Models, Views和Controllers。

  Model组件负责维护应用程序的状态。有时,应用程序的状态是瞬时的,也许只存在于用户和应用程序的交互中;有时,应用程序的状态却是持久的,并且会被保存到应用程序之外,一般是数据库。

  Model并不仅仅是数据,还组织、维护着和数据相关的所有业务规则。例如,对于单价低于$20的商品不能使用打折优惠。模型就应该强制实施这些规则。将业务规则的校验放到Model上是很有意义的,这样就能确保所有的数据都要符合业务规则的要求,可以有效防止非法数据”入境”。Model还扮演着看门人和数据仓库的角色。

  View用于生成用户界面,当然正常情况下要基于Model中数据。比如,在线商城(Online Store)中,商品目录下展示的商品信息。这个商品列表信息就是需要通过Model来获取,然后从Model中取出数据,根据View格式化数据再展示给最终用户。虽然View可以将各种形式获取的数据展示给用户,但是View并不处理用户输入的数据。一旦将数据展示给用户,那么View的工作就做完了。当然,为了不同的目的,可以将相同的数据展示出不同的View格式。在本书所述的在线商城的例子中,一个View用于向用户展示商品目录信息;另外一系列页面让管理员添加、修改商品信息使用。

  Controller来协调整个应用程序。它接受来自外部世界(一般是用户输入)的事件,与Model进行交互,还要选择一个恰当的Views展示给用户。

  Model、View、Controller,这三巨头,合在一起,就是大家耳熟能详的MVC。下图给大家展示着三部分是如何配合工作的:

Figure 4—The Model-View-Controller architecture

  MVC架构最初是为了传统的图形用户界面程序设计的,但是开发人员发现将关注点分离能减少很多程序之间的耦合性,从而使代码更容易编写和维护。每一个概念或动作都可以应用到一个恰当的地方上。使用MVC,就像建造摩天大厦一样,主体已经建好了,完善其余地方就会事半功倍!这里还需要指出一点,在我们应用程序的开发过程中,我们可以大量使用Rails的scaffolding脚本,尽可能多地为我们生成程序所需的组件。

  Rails也是一个MVC框架。Rails强制将应用按照MVC结构分为三部分,你只需要按照划分,分别开发Model、View以及Controller这三个功能模块,然后它们会在执行时,自动相互配合。让人欣慰的是,这三部分的协调工作,不需要任何配置,是由Rails自动整合起来的。这也是Rails的哲学,惯例胜于配置,一个鲜明的例子。

  在Rails中,进来的请求首先被发送给Router,再由Router解析出这个请求该发送给应用的哪部分以及请求该如何处理。最后,这个请求被定为到Controller中的某个方法,在Rails中成为Action。这个Action将会从请求中解析出请求参数,也许还和Model进行交互,甚至有可能调用其它的Action,最后为结构数据准备相应的视图,再渲染给最终用户。

  Rails的处理过程如下图,在这里例子中,当前应用显示的是商品目录页面,用户点击了一个商品的”Add to Cart”按钮,这个按钮将发送连接为http://localhost:3000/line_items?prod-uct_id=2的请求,URL中的line_items是应用中的一个资源,2是我们选择商品的标识符。

Figure 5—Rails and MVC

  路由组件接收到请求后,立即将请求的相关信息进行分解,然后在提取有用的信息。这个请求中,包含一个路径( /line_items?product_id=2))和一个HTTP方法(这个按钮发起的是POST操作请求,其它常用HTTP方法还有GET、PUT以及DELETE)。在这个简单的案例中,Rails将路径的第一部分,line_items,作为Controller的方法,product_id作为商品的标识符。根据惯例,POST方法将惯关联到create() Action上。根据分析结果,Router知道应该调用在LineItemsController类中的create()方法。(我们以后会专门讲解这个命名惯例的。)

  create()方法处理用户的请求。在这个例子中,应用会先查找出当前用户的购物车(其实就是一个用Model管理的对象)。同时,还请求这个购物车Model查找关于商品2的信息。接着,告诉购物车对象,将这个商品添加到购物车中。请注意看Model是如何用来保存业务数据的调用轨迹的?Controller告诉Model该做什么,Model就知道该如何做。

  现在,购物车中包含了一个新商品,我们可以将其展示给用户。Controller将调用view代码,不过在此之前,还要让View可以访问购物车Model中的对象。在Rails中,这些调用都是透明的;没错,惯例有一次帮我们将一个特定的View关联到一个给定的Action。

  这些就是一个MVC架构的网页应用的全部。按照一定的惯例来划分应用的功能模块,你会发现,代码将会变得易于阅读、易于维护;同时,这个应用也变得更加易于扩展和维护。者似乎是一桩不错的交易。

  如果MVC仅仅是一个简单划分代码的方法,那么我们真的需要一个像Rails这样的框架吗?答案也是非常鲜明、直接的:Rails在幕后处理了很多底层工作,尤其是那些麻烦的、需要花很长处理的细节问题,这使得你可以集中精力搞你应用的核心功能。下面,然我们看看Rails是如何处理这些幕后工作的。

Rails对Model的支持

  一般情况下,我们希望将Web应用的信息保存到关系型数据库中。订单系统需要将订单、订单与用户和商品的关联以及用户的详细信息等保存到数据库表中。即使Web应用需要使用不是非结构化信息,比如网志、新闻网等,也会使用数据库作为数据存储的仓库。

  虽然,你不需要立即使用SQL去访问数据库,事实上关系型数据库背后的理论基础是数学中的集合论;虽然,从概念的角度来看,这个非常棒,但是很难将关系型数据库和面向对象编程很好的切合起来。对象的全部是数据和操作,而数据的全部则是一组结构化的数据。虽然,操作在关系方面很容易表述,但是在面向对象系统中却很难编码实现;反之亦然。

  随着时间的推移,人们已经想出了可以协调关系型和面向对象中数据的方法。让我们看看Rails是如何将关系型数据映射到对象中去的。

Object-Relational Mapping

  ORM库会将数据库中的表映射到类中。如果一个数据库中有一个名为orders的表,则我们的程序中就应该有一个名为Order的类。在表中的一条记录对应类的一个对象——一条特定的Orders表中的记录对应一个Order类的一个对象。在对象中,某个熟悉对应获取或者设定某个特定类。我们的Order对象就有专门的方法用于获取或者设置数量(amount)、消费税(sales tax)等等。

  另外,Rails还Model类包装了一组方法,提供对数据库表操作的功能。比如,我们可能需要根据特定ID来查询订单的功能。这个功能已经被作为类方法来实现,方法返回值就是恰当的订单对象。Ruby代码如下:

order = Order.find(1)
puts "Customer #{order.customer_id}, amount=$#{order.amount}"

  有时,这些方法还可能返回一组对象。比如:

Order.where(name: 'dave').each do |order|
   puts order.amount
end

  最后,对应数据库表中特定记录的对象拥有一些用于操作数据库表行记录的方法。比如最常用的方法save(),就是用于将记录保存到数据库中。

Order.where(name: 'dave').each do |order|
   order.pay_type = "Purchase order"
   order.save
end

  所以,ORM层将数据库表映射到类上,行记录映射到对象上,列则映射到对象的属性上。类方法用于执行表级别的操作;而示例方法则执行操作对某一特定行的操作。

  在一个典型的ORM库,您提供的配置数据来指定数据库中的实体和程序中的实体之间的映射关系。程序猿在使用这些ORM工具时,会经常发现他们需要创建和维护一大堆XML配置文件。

Active Record

  Active Record是Rails提供的ORM层。它严格遵守标准的ORM模型:表映射到对象,行映射到对象,列映射到对象属性。和其他绝大多是ORM库最大的不同是关于配置。依靠惯例以及起始与一些合理的默认设置,Active Record可以将开发所需的配置降低到最低。

  为了说明这一点,这里有一个程序,使用Active Record来包装你的订单表:

require 'active_record'
class Order < ActiveRecord::Base
end
  
order = Order.find(1)
order.pay_type = "Purchase order"
order.save

  这段代码展示的是,利用新的Order类去查询id为1的订单,然后在修改其支付类型(pay_type)。(这里,我们省略了创建数据库连接的代码。)Active Record帮我们解决了和不同数据库交互的麻烦,把我们解放出来,使我们可以专注于我们的业务代码。

  其实,Active Record所做的并不仅仅如此。从第五章开始(也就是本系列下节的内容),将开发一个购物车应用,Depot,到时你就会看到,Active Record可以无缝整合Rails框架的其余部分。如果网页表单发送应用程序的的数据给业务对象,Active Record将会从请求中提出数据,然后传递给Model;Active Record还可以说实现复杂、精密的模型数据校验,如果表单数据校验失败,则Rails的View还可以将错误提取,并展示出来。

  Active Record是Rails MVC架构中,实体模型的基础。

Action Pack: View和Controller

  如果你注意到View和Controller,你就会发现它们之间结合得是如此严丝合缝。Controller向View提供数据;同时,Controller还接受页面发起的事件,而这些页面又是View生成出来的。正式由于二者结合的如此紧密,Rails将二者绑定到同一个组件中,而这个组件就是Action Pack。

  不要傻傻地自以为Action Pack是一个组件,而会将View和Controller的代码交织到一起。恰好相反,Rails将你开发的网页应用清晰地划分为Controller和展示页面。

对View的支持

  在Rails中,View负责创建所有或者部分响应,这些响应或者在浏览器中展示,或者由应用程序处理,或者作为Email发送出去。最简单的情况就是,由展示某些文字的一堆HTML代码组成一个View。更具代表性的是,View中会包含由Controller中的Action方法动态生成的内容。

  在Rails中,动态内容是由模板生成的,有三种模板可供我们选择。最常用的模板是,被称为嵌入式Ruby(Embedded Ruby,简称为ERb),在视图文件中,潜入一些Ruby片段,这种方式和其他一些技术,例如JSP、PHP等很像。虽然这种方式很灵活,但是需要注意的是,这同样和MVC的精髓相违背。因为视图文件中有潜入的Ruby代码,那么我们就必须冒着增加业务代码的风险,也许原本这些业务代码应该在Model或者Controller中。像其他事物一样,这里有个度,或恰到好处,或过犹不及。维护一个清晰的关注点的分离情况也是开发工作的一部分。(以后章节中,我们会看到HTML模板以及实用ERb来生成HTML。)

  你同样还可以使用ERb在服务端生成JavaScript代码,然后在浏览器中执行。这对于创建动态的Ajax接口,是非常棒的功能。同样,这部分内容,以后再详细介绍。

  Rails还提供XML构造器(XML Builder)来构造XML文档,生成的XML结构自动和代码结构保持一致。这个功能以后再详细介绍。

Controller

  Rails的Controller是整个应用的逻辑中心。它负责协调用户,View以及Model之间的交互。Rails会处理大量的幕后工作,你只需要写应用程序的功能相关的代码即可。这使得Rails的Controller代码非常容易开发和维护。

  Controller还提供了不少终于的附近功能

  • 它可以处理从Router来,到Action的请求;同时可以控制生成非常友好的URL:
  • 它维护应用的缓存,可以将应用的性能提升几个数量级;
  • 它负责维护一个模块辅助器(helper modules),可以在不增加代码量的情况下,扩展视图模板的功能;
  • 它还负责维护会话(Session),可以保障用户与应用连贯的交互体验。

  我们在上一节已经查看并修改过Controller代码。在以后的章节中,我们还会看到更多的关于Controller的内容。

未完待续

  下一节我们将开始介绍本书的购物车示例。敬请关注!

吐槽

  翻译这活真心不好干!英语不好伤不起啊!

  翻译的确是不好,如果发现什么错误,请留言,D瓜哥马上改正。



作 者: D瓜哥,https://www.diguage.com/
原文链接:https://www.diguage.com/archives/7.html
版权声明:非特殊声明均为本站原创作品,转载时请注明作者和原文链接。