来源:Philosophies that Shaped Successful Frameworksjavascript
在过去的十年里咱们看到了许多软件框架的出现,像 Spring 和 Ruby on Rails 已是很是成功的框架了,掌握它们就意味着打开多扇就业机会的大门了。然而,对于每个框架的成功,背后的大多数开发人员都不被人关注。2008年1月1日维基百科 列出了67个 Web 框架。然而今天,超过三分之二的消失在列表中或在三年内没有更新。做为 Yii 框架的创造者,我花了不少时间调查各类框架和理解为何有些成功,有些失败了。我将描述我发现塑形成功框架的一些哲学。php
创建一个成功的框架,重要的是要了解什么是框架,开发人员为何须要它们。html
Douglas C. Schmidt 等人 认为框架做为一个集成的软件构件(如类、对象和组件)集合,为相关应用程序提供一个可重用的体系结构。根据这必定义,
框架应该是一个已完工的应用骨架组成可重用和可定制的组件。开发人员将扩展并定制一个框架经过提供他们的应用程序和领域特定逻辑来造成一个完整的应用程序。java
一个框架典型的特征就是所谓的控制反转(inversion of control)。框架一般扮演着组织主程序的角色和调用应用程序代码。这里是反过来的控制流——它调用我而不是我调用框架。下图说明了框架之间的关系,函数库,和应用程序。注意框架一般提供现成的功能的库,以帮助开发人员构建应用程序更快。python
开发人员使用框架最重要的缘由是框架如何提升生产力和帮助提升代码质量。例如,现代的框架(例如,django),常常提供代码生成工具或样板帮助当即启动新项目。此外,精心设计的框架内嵌安全保护措施,帮助预防开发人员犯典型的安全漏洞。web
企业使用框架,还有一个额外的好处是,它能够应用在整个企业,帮助执行标准。框架提供了记录模式,详细的设计和实现的工具用于在全部应用程序之间提供一个一致的结构。例如,在 Capital One (译者注:薛强所在的公司) 咱们开发一个 「Chassis」的框架做为一个集成的基础,统一了许多厂商和顾客公司内部开发应用程序的 API。spring
固然,并非全部的开发人员喜欢使用框架。一些一致的抱怨包括陡峭的学习曲线,框架耦合性比较高,性能较低,等等。今天,在这篇文章中我将为你解释现代框架如何的解决这些问题,让大多数的这些抱怨再也不适用。sql
<!--more-->数据库
像任何一个产品同样,一个框架的成功取决于许多因素,包括其背后的思想,代码质量、文档,周围社区,营销,支持,等等。在我看来,特别重要的一项是考虑当一个框架被设计和开发的哲学。express
很久之前 Python 开发者 Tim Peters 开发 Python 时发表了被称为 Python 之禅 的二十格言设计原则。『优美胜于丑陋,明了胜于晦涩,简洁胜于复杂……』他们鼓舞了许多相似的编程语言之禅(similar programming language zens),我发现这些格言是适用于框架设计的。根据个人框架开发经验,我特此冷凝和总结我认为任何成功的框架最重要的哲学。
越简单越好
总体设计是最糟糕的
一致性
明了胜于晦涩
约定大于配置
让开发人员转换一个新的框架历来都不是一件容易的事。然而,当开发人员选用框架,会做为重点依靠它来投资当前和将来的项目。此外,不想使用类库 - 开发人员能够学习一个 API 实现它 - 学习框架要求开发人员在投入实际使用以前要充分理解框架规则。所以,重要的是要确保简单的设计一个框架,使它更容易、有趣,并且容易去学习,接受和利用。
为了实现简单,一个框架应该强制执行必定数量的限制规则;同时,这些规则应以统一的方式设计和有良好的文档记录。框架执行更多的规则,陡峭的学习曲线,让开发人员很难接受。当规则是一致的,开发人员能够更快学习它们。没有文档,一个框架是无用的。由于没有人会花时间反向工程其规则。
Express.js framework 框架路由语法规则的设计是一个很好的例子,一个很是受欢迎的 web 应用服务器框架。而路由在 web 应用程序中是一个重要的概念,是肯定应用程序如何响应客户端请求一个特定的端点(一个HTTP方法和一个URI)。Express.js 介绍一个简单的规则来定义一个路线,app.METHOD(PATH, HANDLER)
,METHOD
是一个 HTTP 请求方法(例如 GET、POST),PATH
是服务器上的一个 URI 路径,HANDLER
是回调函数路线相匹配时要执行的。下面的代码片断显示了 Express.js 路由代码的样子。
var express = require('express'); var app = express(); // accept homepage request app.get('/', function (req, res) { res.send('Hello World!'); }); // accept POST request at /user app.post('/user', function (req, res) { res.send('Got a PUT request at /user'); }); // accept DELETE request at /user app.delete('/user', function (req, res) { res.send('Got a DELETE request at /user'); });
上面的代码是不言自明的,由于它像是如何去看一个 HTTP 请求。所以,开发人员只须要不多的努力去学习就记住这个路由语法而且把它的实用性应用到本身的项目中。
这里的术语「总体」指的是以一个以紧密耦合的代码库为基础构建的框架。web 框架刚开始流行时,他们每每是一个总体,由于他们的主要目标是提供全方位的快速的 web 应用程序开发。渐渐地,人们意识到总体框架有不少问题。例如,
即便改变是框架彻底无关的一小部分须要从新测试和释放整个,从而致使应用程序的框架要重建。实际上,总体框架的中代码耦合使得它很是难以保持不一样版本的向后兼容性。好比专业缓存、日志、数据库,人们变得不那么愿意被绑定到一个单一的总体框架。
现代框架每每是松散耦合的体系结构。全栈框架(例如 Spring )已经演变成由松散耦合的组件能够单独使用或与第三方交换的框架。专门的框架是有明确的契约,以支持更好的互操做性,这使得应用程序不依赖于特定的框架。例如,一个很是受欢迎的 web 路由框架的特色是所谓『Sinatra-type 框架』,如 Sinatra,Express.js 和 Martini。这些框架使用如下中间件管道架构支持请求路由和处理web应用程序。框架自己是很是小的,但开放式体系结构容许他们无限丰富的各类中间件组件。
一致性意味着一个框架,坚持使用统一的设计,命名约定,代码风格,代码组织等等。一个一致性的框架将下降门槛,由于用户能够学习框架一个方面,而且应用相同的模式,去快速学习其余的结构。一致还能够帮助用户减小框架特征错别字或误用的可能性。
例如,当设计 Yii 框架的 query builder ,咱们把一致性做为一个指导标准。查询构建器(query builder)容许您以编程方式建立一个数据库无关的 SQL 语句,避免 SQL 注入攻击。为了帮助用户更容易地记住它的 API ,咱们介绍了链式接口和命名后相应的 SQL 关键字的方法。下面的代码片断显示了如何使用 SQL 语句查询构建器设计。
(new Query()) ->select('id, email') ->from('user') ->orderBy('last_name, first_name') ->limit(10) ->all();
上面的代码将生成和执行 MySQL 声明以下:
SELECT `id`, `email`FROM `user`ORDER BY `last_name`, `first_name`LIMIT 10
正如您能够看到的,代码读取很是相似于你编写 SQL 语句。查询构建器之间的一致性和 SQL 语法很容易学习查询生成器。
关于编写本身的代码显式大于隐式,避免过多的使用 “自动魔法”,有两个缘由坚持这种哲学。首先,显示的代码更容易理解和维护。因为代码是自解释的,维护人员可能不是代码的原做者,不须要来回跳转找到实际上执行的代码。其次,显示的代码不容易出错。虽然显示的可能须要编写更多的代码行,它减小了看似简单含蓄确笼罩着重要的代码的状况。
看看下面的两个 ORM (对象关系映射) 在 PHP 的代码。他们都但愿实现『订单』数据库记录和『客户』DB 记录之间创建外键引用约束的相同的目标。
$order->link('customer', $customer);
与
$order->customer = $customer;
第一个版本是正常的方法调用。第二个版本看起来更酷,由于复杂的数据库链接操做能够经过一个看似简单的任务来完成。然而,这是一种错觉,第二个版本的简单性是由其余地方的的复杂性隐藏掩盖。例如,用户不得不经过某种形式的文档来学习这种特殊的赋值语法,以便在实践中使用它。由于连接操做看起来像一个正常分配时,用户可能会忘记处理由它引发的潜在的异常,从而致使整个程序发生故障。
事实上,Yii 的发展过程当中,关于两个版本咱们讨论了不少,并最终选定了第一个版本,它已收到投诉不多。
约定大于配置的概念已经存在好多年了。这个想法是一个框架应该采起坚持的公约,遵照约定同时仍然容许经过配置提升扩展性。决策的目标是减小开发人员须要作的数目,从而实现哲学# 1——简单性。
约定大于配置最先在 Ruby on Rails 框架中开始流行。Rails 提供一个 ActiveRecord 库,用类和数据库中的表之间的映射处理。按照惯例,表名是类名的多元化形式。所以,该类帐户将有一个表称为账户。若是该表不命名这种方式,用户将必须显式配置类名和表名称之间的映射关系。
许多 MVC 框架使用约定大于配置请求路由到特定的代码片段。以下图所示,Sails.js framework 框架使用的约定,其中的 /we/say/hi
URL请求将被路由到controllers/we
目录下 SayController 控制器类的hi
动做。按照本约定,开发人员再也不须要对控制器的行为定义路由规则。可是,若是开发人员想要使用一个不一样的路由规则,他们仍然能够经过显式绑定一个路由到一个控制器动做。
约定优于配置有助于减小须要编写的代码量。然而,它会给开发人员须要遵照规则引入了额外的成本。同时,也每每与前面讨论的『显示大于隐式』的哲学相冲突。事实上,虽然早期版本的 Spring 框架使用了似的 Sails.js 路由约定,Spring 如今要求开发者经过注释明确指定映射。所以,当决定是否引入新的规则以支持约定大于配置,应采起明智的判断。
建设一个成功的框架是全部关于功能和简洁性之间的平衡。整个构建框架的过程当中,取舍常常须要以坚守,并举例说明,上述哲学加以考虑。
有时候,你可能会遇到其中一个理念是与另外一个直接冲突的状况。一致性是比简单更重要?在约定比显性更重要?在这种状况下,请记住,一个框架的最终目标是简化开发人员的工做,并简化代码的编写进程。所以,保持它的简单和直接。若是他们有明确性冲突,由于前者会带来隐藏的复杂性能够牺牲约定。一样,若是坚持一致性能够稍微违反严格会形成额外的并发症。
Posted Dec 15, 2015 by...
Qiang Xue
软件工程师LEAD、技术人员