该书下载自-书部落-分享计算机经典巨著!-www.shubulo.com仅供试看^∧ 第2章 Rails应用的架构 The Architecture of Rails Applications 说到 Rails,有一事不能不提:它对“如何组织web应用的结构”这件事有着相当严 格的约束。真正有趣的是,这些约束反倒使得应用程序的创建更加简单—简单得多。 现在,让我们来看看这是为什么。 2.1模型,视图,以及控制器 Models. Views, and Controllers 回想1979年, Trygve Reenskaug提出了一种开发交互式应用的全新架构。在他的设 计方案中,应用程序被分为三类组件:模型、视图,以及控制器。 模型( model)负责维持应用程序的状态。有时候这种状态是短暂的,只在用户的几 次操作之间存在:有时候这种状态则是持久的,需要将其保存在应用程序之外(通常保 存在数据库中)。 模型携带着数据,但又不止是数据:它还负责执行施加于这些数据之上的业务规 则。譬如说,“对于20元以下的订单不予打折”这一约束就要由模型来确保。这种做法 是很有意义的。由于将业务规则的实现放进模型中,我们可以确信应用程序的其他部分 不会搞出非法的数据来。模型不仅是数据的容器,还是数据的监护者。 视图(wew)负责生成用户界面通常会根据模型中的数据来生成。譬如说, 个在线商店可能需要将一系列商品显示在屏幕上。通过模型可以访问这个商品列表,但 还需要一个视图,它通过模型访问商品列表,并将其格式化为最终用户能够理解的形 式。视图可能允许用户以多种方式输入数据,但输入的数据一定不由视图本身来处理, 视图的唯一工作就是显示数据。出于不同的目的,可能会有多个视图访问同一个模型 Web开发敏捷之道一应用Rais进行敏捷Web开发,第2版
2童 Rails The Arch it ect u re of Rails Applications Rails. 'II 不能 它对 何组织web 有着相 格的约束.真正有趣的是,这些约束反倒使得应用程序的创建更加简单一一简单得多. 现在 们来看看这是为付么 2.1 M odels, Views, and Controllers 7 9 Trygve ee 出 了 种开发交 式应 架构 案中 应用程 分为 组件: 型、视剧 ,以及控制器 模型 /) 责维持应 程序 候这种 短暂 次操作之间存在.有时候这种状态则是持久的 ,需要将其保存在应用程序之外(通常保 存在数据库中) 模型携 着数据.但又不 止是数 责执行施加于这些数据之上的业务规 则.譬如说, "对于 0元以下的订单不予打折 "这一约束就要由模型 确保. 这种做法 是很有意义的.由于将业务规则的实现放进模型中 ,我们可以确信应用程序的 他部分 不会搞出非法的数据来.模型不仅是数据的容器,还是数据的监护占. 视图 责生 根据模 说, 个在线商店可能需要将←系列商品显示在屏幕上.通过模型可以访 个商 列表 还需要一个砚图 ,它通过模型访问商品列表 .并将其格式化为最终用户能够理解的形 式.视图可能允许用户以多种方式输入数据 ,但输入的数据定不由视图本身 处理 视阳的唯 示数 同的目的, 可能会有多个视罔访问同一个模 Web JfJtIt 2Jl! &, it! 11f eb 开发 Jt& 该书下载自-书部落-分享计算机经典巨著!--www.shubulo.com!仅供试看^_^
该书下载自-书部落-分享计算机经典巨著!-www.shubulo.com仅供试看 12p第2章Rais应用的架构 在我们的在线商店中,就有一个视图显示分类的商品信息,还有管理员使用的视图用于 添加和编辑商品信息。 控制器( controller)负责协调整个应用程序的运转。控制器接收来自外界的事件(通 常是用户输入),与模型进行交互,并将合适的视图展示给用户 这个三位一体的组合—模型、视图和控制器—构成了一个架构模式,那就是著 名的MvC。图2.1大致描述了MVC架构的概念。 O Browser sends request ② Controller intera (3 Controller invokes view Controller ④ View renders next browser screen Model Database 图2.1模型-视图控制器架构 MVC最初是计划用于传统的GUI应用程序中的。开发者们发现:通过分离关注 点,可以大大降低系统的耦合度,使代码易于编写、易于维护。每个概念、每个动作都 只在一个众所周知的地方描述。使用MVC开发应用程序,就好像在搭好的桁架上盖大 楼一样—只要结构已经到位,搭建其他部分就容易多了。 在软件世界里,我们常常会在向着未来猛冲的时候忘记来自过去的宝贵财富。当开 发者们刚开始编写web应用时,他们又回到了一个原始时代:表现、数据访问、业务逻 辑、事件处理都被塞进一大堆代码中。不过,过去的好思想还是慢慢露出了头,人们开 始尝试将20年前的老套路—MC—用到web应用的架构中来。其结果就是诸如 WebObjects、suts、SF之类的框架,它们全都建立在MvC的理念之上(不过又或多 或少地有自己的发挥)。 Web开发敏捷之道一应用Rais进行敏捷Web开发,第2版
12 ' Rails 的架构 在我们的在线商店中 ,就奋一个视图显示分类的商品信息,还布管理员使用的视图用于 添加和编辑商品信息 制器 ntroll 整个 程序 运转 制器接收来 界的 常是用户输入 .与模型进行交豆,并将合远的视图展示给用 这个三位一体的组合一一模型、视图和控制器一一构成了 一个架构模式,那就是著 名的 .图 1大致描述了 C架构的概念 Data ba se ro sends 町uest ( Controller interacts with model ( Controller invokes view Controller ( View renders next browser screen --明. - E = 模型-视图·控制器架构 是 计 于传统 UI 发者 ,可以太大降低系统的精合度,使代码易于编写、易于维护.每个概念、每个动作都 只在一个众所周知的地方描述.使用 C开发应用程序,就好像在搭好 街架上盖大 一样一一只要结构已经到位,搭建其他部分就容易多了 在软件世界里,我们常常会在向着未来猛冲的时候忘记来自 过去的宝贵财富。当开 发者们刚开始编写 b应用时,他们又回到了一个原始时代表现、数据访问 、业务逻 辑、事件处理都被塞进-大堆代码中 .不过,过去的好思想还是慢慢露出了头,人们开 始尝试将 0年前的老套路一 -周到 b应用的架构中来.其结果就是诸如 WebO je ts 、Struts 、JSF 框架 它们全部建立在MVC 理念之 或少地有自己的发挥) • Web JtIN ill 0, Rail {j. WebHJt ~曹 11N 该书下载自-书部落-分享计算机经典巨著!--www.shubulo.com!仅供试看^_^
该书下载自-书部落-分享计算机经典巨著!-www.shubulo.com!仅供试看^A 2.1模型,视图,以及控制器“13 Ruby on Rails也是一个MvC框架。 Rails强迫你将应用程序按照模型、视图和控制 器进行划分,并遵循这一结构分别开发各部分的功能。当程序运行时, Rails s会把各个部 分组装在一起。Rail的有趣之处在于:“组装”的过程默认地按照人们常用的命名惯例 来进行,因此,一般情况下你不需要编写任何外部的元数据配置信息。这正是Rals一以 贯之的“惯例重于配置”观念的体现。 在一个 Rails应用程序中,进入的请求首先被发送给一个路由组件,该组件判断应该 将请求发送到应用程序的什么部分、如何解析这一请求。这一阶段将找出控制器代码中 的某个特定方法,要求它来处理请求(用Rail的行话,这个方法叫做“ action”)。 action 可以查阅请求中携带的数据,可以与模型交互,也可以调用别的 action。最后, action会 为视图准备充分的信息,视图则将所需的信息展现给用户。 图2.2展示了 Rails处理一个请求的全过程。在这个例子中,我们假设应用程序已经 向用户展现了一个“产品分类列表”页面,用户则点击了某个产品旁边的 Add To Cart(“放 进购物车”)按钮。这个按钮链接到我们的应用程序,URL是http://my.url/store/ add to cart/123,其中的“123”是所选商品在系统内部的I号。 Routing Ohttp://my.url/store/add_to_cart/123 ② (2 Routing finds Store controller (3 Controller interacts with model Store 6 View renders next browser screen Controller Display Cart Record Database 图22 Rails与Mvc 在本书的后面部分,我们会详细介绍Rals应用中URL的格式。不过,有必要在这里指出:通过URL来执行诸如”“放 进购物车”这样的动作可能并不安全。详情请参阅第461页,第21.6节“GET请求的问题” Web开发敏捷之道一应用Ras进行敏捷Web开发,第2版
2 .1 畏型,视圈 ,以及控市j器 " 13 Ruby on Rail 个MVC . . Rails 程序按照模 器进行划分,并遵循这一结构分别开发各部分的功能.当程序运行时, Rails 各个 分组装在一起 趣之处在于z "组装"的过程默认地按照人们常用的命名惯例 来进行, 因此, 一般情况下 需要编写任何外部的元数据配置信 .这正是 i l 一以 之的 "惯例重于配置"观念的体现 一个 s应用程序中,进入的请求首先被发送给-个路由组件,该组件判断应该 将请求发送到应用程序的什么部分、如何解析这一请求.这一阶段将找出控制器代 的某个特定方法,要求它来处理请求(用 ai s的行话,这个方法叫做,.action " ) .. ac tion 可以查阅请求中携带的 据 ,可 以与模型交 也可以调用 别 的 .. 政后 action 为砚阁准备充分的信息,视剧则将所需的信息展现给用户 2展示了 il s处理一个请求的全过程.在这个例子中,我们假设应用程序已经 向用户展现了一个"产品分类列表"页丽,用户则点击了某个产品旁边的 To Can(" 进购物车")按钮.这个按钮链接 的应用程序 URL t t / / s t r e add_t 一cart /123 2 3 所选 部的 ID 气一 - ....一- y 旦旦旦且 :limy.url lstor 旭dd_t cart/ t 23 ( Routing finds Store ll ( Controller interacts with model ( Controller invokes view ( View renders next browser screen • J Database I Ra 与MV 部分 j\, I. 在边 I. 情如 进购物牟"迫样的动作可能并不安全.详情请参阅 1页 .革 1. ET情卓的问题. Web!fJt111l il 一-.Mb il 'IitJ# Web !fJt Jt5 该书下载自-书部落-分享计算机经典巨著!--www.shubulo.com!仅供试看^_^
该韦下载自苇部落分享计算机经典巨著! ww shubule ee朏钗供试看 14p第2章Rais应用的架构 路由组件收到来自外部的请求之后,立即将其拆成小块。简单来说,它把路径的第 1部分(“ store”)看作控制器的名称,第2部分(“ add to_cart”)看作 action的名称, 最后一部分(“123”)则按照惯例被放入一个名为“ia”的内部参数。进行这样的分析之 后,路由组件就知道:应该调用 Storecontro11er这个控制器类中的 add to cart() 方法(我们在第235页还将详细介绍 Rails采用的命名惯例)。 add to cart()方法会处理用户的请求:找到当前用户的购物车(这也是由模型管 理的一个对象),请求模型找出编号为123的商品信息,然后告诉购物车将该商品加入 其中(请留意模型是如何跟踪所有业务数据的:控制器只是告诉它做什么,而模型自己 知道该怎么做)。 现在,购物车中已经放进了用户刚选的商品,我们需要将这一事实展现给用户看 控制器会安排视图访问模型中的购物车对象,并调用视图代码使之呈现在用户眼前 在 Rails中,这一调用通常都是隐藏在幕后进行的。对于特定的 acton, Rails将根据命名 惯例自动为其查找一个特定的视图。 这就是一个 MVC web应用的全部家当了。只要遵循一定的命名惯例,并且合理划 分功能,你会发现编写代码变得轻松愉快,你的应用程序会更具可扩展性、可维护性。 看起来确实挺划算的 如果MVC仅仅是“以某种方式划分代码”的话,你可能会想,那还要 Ruby on rail 这样的框架干什么?答案很简单: Rails帮你搞定了所有低级的基础代码—所有那些需 要耗费你大把时间去处理的繁琐细节。它让你能够专注于应用程序的核心功能。现在, 让我们来看看它是如何做到这一点的。 22 Active Record: Rails的模型支持 Active Record: Rails Model Support 般来说,我们都希望将web应用中的信息保存到关系数据库中。一个订单系统会 在数据库表中存储订单、订单项、客户信息等数据。即便是处理大量非结构文本的应用 (譬如blog和新闻网站),也常常会用数据库作为后端数据存储介质 虽然从通常使用的SQL2中不大看得出来,但关系数据库确实是根据数学中的集合 理论设计出来的。从概念的角度来说这是件好事,但它也使得关系数据库与面向对象编 2sQL:结构化查询语言( Structured Query Language),用于读、写关系数据库的语言 Web开发敏捷之道一应用Rals进行敏捷Web开发,第2版
14 i l 的架 路由组件收到米自外部的请求之后 ,立即将其拆成小块.简单来说,它把路径的第 看作 的名 部分 add_t o_cart 看作 的名 称, 革后一部分 j! 按照惯 被放入 名 为 id 部参数 进行这 分忻之 ,路由组件就知道=应该调用 e C o l l r这个控制器类中的 (l 方法〔 们在 5页还将详细介绍 s采 的命名惯例) 0 add_to_cart () 会处理用户 请求 到当前用 购物车(这 也是 理的一个对象) 请求 号 为 然后告 其中 (请留意模型是如何跟踪所 业务数据的: r~~制器 告 诉 而愤型 知道该怎么做) 现在,购物军中已经放迸了用户刚选的商 们需要将这 ji实展现给用户肴 控制器会安排视阁访问模型中的购物东对象,并调用视同代码使之呈现在JIJ户眼前 s中 ,这一调用通常都是隐藏在幕后进行的.对于特定的 ail 根据 惯例 动为其查 的视 这就是一个 eb 全部 只 要遵循 且合 分功能,你会发现编写代码变得轻松愉快,你的应用程序,会更具可扩展性、可维护性 起来确实挺划算的 如果 C仅仅是 "以某种方式划分代码"的话,你可能会想,那还要 by on Rails 这样的框 干付 简单 fIl你搞定 所有低级的基础代码所有那些需 税费你大把时间 处理的繁琐细节.它让你能够专注于应用程序的核心功能.现在, 们来看看它是如何做到这一点的 2.2 Active Record: Rail 支持 Active Record: Rails Model Support 来说 都希塑将 用 中 息保 关系数据 中.一个订单 在数据库表中存储订单、订单项、客户信息等数据.即使是处理大量非结构文本的应 (譬如 og 和新闻网站) 用 数 端数 介质 虽然从通常使用的 '中不大看得出来,但关系数据库确实是根据数学中的集合 从概念 的角 度来说这 件好 .但它也使得关系数据库 面向对象编 2 QL 结构也董 ru rt' Que ry I1 Web «HLilf Dlflj Rails IJfilt1# Web Jf J:N 该书下载自-书部落-分享计算机经典巨著!--www.shubulo.com!仅供试看^_^
该书下载自-书部落-分享计算机经典巨著!-www.shubuc.com仅供试看 22 Active Record:Rals的模型支持 程语言很难融为一体:对象关注的是数据和操作,而数据库关注的则是值的集合。所 以,用关系的术语很容易描述的事情有时在OO体系中很难编码实现,反之亦然。 在长时间的实践中,人们想出了很多办法使关系模型与OO模型得到协调。下面我 们将看到两种不同的途径,其中之一要求以数据库为中心组织应用程序,另一种则以应 用程序为中心组织数据库 数据库为中心的程序设计 Database-centric Programming 第一批针对关系数据库编程的程序员使用的是过程性语言,例如C和 COBOL。这 些家伙通常直接在代码中嵌入SQL可能是将SQL语句作为字符串嵌入,也可能是使 用预处理器将源码中的SQL编译成针对数据库引擎的低级调用。 这种集成意味着:数据库逻辑会很自然地与整个应用程序的逻辑纠缠不清。如果开 发者想要找出所有的订单,并更新所有订单的销售税,他就会写出一些非常丑陋的代 码,譬如 EXEC SQL BEGIN DECLARE SECTION int id: float amount EXEC SQL END DECLARE SECTION: EXEC SQL DECLARE cl AS CURSOR FOR select id, amount from orders while (1) float tax: EXEC SQL WHENEVER NOT FOUND Do break; EXEC SQL FETCH C1 INTO id, amount: tax calc sales tax(amount) EXEC SQL UPDATE orders set tax tax where id= id: EXEC CLOSE C1 EXEC COMMIT WORK 很恶心,对吧?别担心。我们不必干这种事—虽然这种编程风格在Per和PHP之 类的脚本语言中很常见。当然,用Ruby你也可以这样写程序。譬如说,我们可以使用 Ruby的DB库写出类似的代码(下面的示例代码—和前面那段一样—没有任何错误 检查)。 definitic def update_ sales_tax update edb prepare("update orders set tax=? where id=?") edb, select all("select id, amount from orders") do id, amount I tax calc sales tax(amount) update. execute(tax, id) Web开发敏捷之道一应用Rals进行敏捷Web开发,第2版
2 .2 Active 。rd: Rails 支持 程语言很难融 象关注 是数据和操 而数据库关注 则是值 合. ,用关系的术语很容易描述的事情布时在 0体系中很 编码实现,反之亦然. 在长时间的实践中,人们想出了很多办法使关系模型与 00模型得到协调 .下面 们将看到两种不同的途径,其中之 要求以数据库为中心组织应用程序,另 一种则以应 用程序为中心组织数据库 数据库为中 程序设 Database-centric Programming 一批针对关系数据库编程的程序员使用的是过程性语言,例如 to L. 些家伙通常'亘接在代码中嵌入 L一一可能是将 L语句 为字符 嵌入 能是使 用放处理器将源码中的 L编译成针对数据库引擎的低级调 这种集成 意味 -数据库逻辑会 恨自然地与整个应用程序的逻辑纠缠不清 。如果开 发者想要找出所育的订 并更新所有订单的销售税,他就会 写出 一些非常 丑陋的 EXEC SQL BEGIN DECLARE SECTION; i nt id ; f l oat amount; EXEC SQL END DECLARE SECTION; EXEC SQL DECLARE cl AS CURSOR FOR select id , amount from orders: while (1) { float tax ; EXEC 口L WHENEVER NOT FOUND 00 break; EXEC SQL FETCH cl INTO : id, :amount ; tax: calc_sales_tax{amountl EXEC SQL UPDATE orders set tax = : t a x where id =立 } EXEC SQL CLOSE eli EXEC SQL COMMIT WORK; 很恶心,对吧?别担 .我们不必 干这种$--虽然这 种编程风 P之 类的脚本 言中很常见 然, y你也可以这样 写程序 譬如说, 我们可以使用 by DBI 下面 示例代码 而那段 def update_sales_tax update = @db.prepare(·update orders set tax=? where id=?-) @db.select_all(·select id, amount from orders ·) do lid. amountl tax = calc_sales_tax(amount) update.execute(tax, id) e nd end Method del " 635 :1 - WebHJtIf. 一-N,房Rails i!!F.i1N1# Web .!J 第2 1!N 该书下载自-书部落-分享计算机经典巨著!--www.shubulo.com!仅供试看^_^