1.8 程序设计最佳实践
开发应用程序软件是复杂的。一些有助于管理复杂性的方法非常流行,以至于它们被赋予容易记忆的首字母缩略词。接下来,我们就会介绍一些这样的规则,并介绍如何在服务器程序设计时更好地遵守这些规则。
1.8.1 KISS——尽量简单(keep it simple stupid)
成功的程序设计的一个重要技术就是编写简单的代码。也就是,你编写的代码3年以后仍然可以很容易理解,并且其他人也可以理解。这种方式并不一定总是行得通,但是尽可能用最简单的方法编写代码总是有意义的。由于各种原因,比如速度、代码压缩或者炫耀你有多聪明等,你都有可能再次编写这些代码的某些部分。同时,总是以简单的方式编写代码,能让你绝对相信这些代码就是你想要的。这样做不仅让你的编程工作更加快捷,同时当你想尝试更多高级的方法去完成同样的事情时,你也可以有对比的东西。
并且记住,调试比编写代码更加困难。所以如果你把代码写得非常复杂,在调试代码时你会非常头疼。
编写一个返回函数的集合通常比一个复杂的查询更容易。是的,这样的函数可能比通过单个复杂查询语句实现的方式运行更慢,原因是对于作为函数编写的代码,优化器的作用很有限,但是这样的速度应该足以满足你的需求。如果你需要更快的速度,你可以一点点重构这段代码,将部分函数加入到更大的查询中,这样优化器就可以获取更好的查询计划,直到运行效率再次变得可以接受。
记住,在大多数情况下,你并不需要最快的代码。对于你的客户或者老板,最好的代码是及时有效地完成工作。
1.8.2 DRY——不要写重复的代码(don’t repeat yourself)
这点可以理解为努力保证每个业务逻辑的代码段都编写一次,并且把完成任务的代码放到正确的位置。
这样做可能会有些难度,例如,你想对浏览器的Web表单做一些检查,但是在数据库里还会做一次最后的检查。但是作为一个通用准则,它仍然是非常有效的。
服务器程序设计这时候就派上用场了。如果你的数据操作代码位于贴近数据的数据库内,所有用户便可容易地访问数据,这样你就不需要管理一份相似的代码,这些代码可能是用一个C++Windows程序、两个PHP网站和一个Python脚本的分支开发的,并用来完成每天的管理任务。如果它们中的任何一种方式需要对一个客户表做些什么事情,它们仅仅需要调用:
这样就搞定了!
1.8.3 YAGNI——你并不需要它(you ain’t gonna need it)
换句话说,不要做得比你确实需要的多。
你是否有种可怕的感觉你的客户并没有很好地注意到最终的数据库是什么样或数据库可以做什么,那么你必须坚持在数据库中所设计的一切是有益的。一个更好的办法是完成最小的开发,来满足当前的要求,但是在思路上要具有扩展性。当使用巨大的设想去实现一个大的产品要求时,很容易让自己陷入困境。
如果你通过函数来组织对数据库的访问,就很可能对业务逻辑做更大的修改,而不需要涉及前端应用程序代码。即使你已经5次改写这个函数并且两次改变过整个表结构,你的应用程序仍然只须执行SELECT * FROM do_this_thing_to_customers (arg1, arg2, arg3)。
1.8.4 SOA——服务导向架构(service-oriented architecture)
最初听说SOA通常来自于企业软件人员向你销售一组复杂的SOAP服务。但是SOA的本质是把你的软件平台组织为一套服务,这样客户端和其他服务就可以调用并执行某些定义好的原子任务,例如:
检查用户的密码与证书
给用户呈现他喜欢的网站列表
向用户售卖新的红色小狗项圈(附赠红色项圈小狗俱乐部的候补会员资格)
以上这些服务可以通过SOAP调用来实现,SOAP调用同时带有相应的WSDL定义、包含servlet容器的Java服务器和复杂的管理框架。它们也可以是一系列PostgreSQL函数,这些函数带有一组参数并返回一组值。如果参数和返回值比较复杂,它们可以作为XML或JSON来进行传递。但是通常情况下,一组简单的标准PostgreSQL数据类型已经足够使用。在第9章中,我们将学习如何让这种基于PostgreSQL的SOA服务变得可以无限
扩展。
1.8.5 类型的扩展
前面阐述的一些技术同样可用在其他的数据库系统上,但是PostgreSQL的扩展性绝不只限于这些内容。在PostgreSQL中,可以使用任何一种较为流行的脚本语言来编写用户定义函数(UDF)。也可以定义自己的类型,不仅仅局限于附有额外约束的标准类型,还包括其他成熟的新类型。
例如,荷兰的一家公司MGRID已经开发出衡量单位大小的数据类型,这样你就可以将10km除以0.2小时,得到50km/h的结果。当然,也可以将同样的结果转换为米每秒或者任何其他表示速率的单位。当然,也可以把它当做c(光速)的一小部分。
此类功能同时需要类型本身和重载后的操作数,这样如果你用距离除以时间,就可以知道结果是速率。你也需要用户定义的转换操作,它们是类型间自动或者手动调用的转换函数。
MGRID开发这个数据类型是为了在医疗应用中推广使用,因为医疗应用中的误差成本实在过于昂贵——10ml和10cc就会是致命的区别。但是通过使用一个相似的系统就可以避免许多灾难,比如使用了错误的单位就会输出糟糕的计算结果。如果这个单位总是和量同时出现,那么这种错误出现的可能性就几乎为零。当现有的指标已经无法解决你的问题时,如果你自己具备程序设计的能力,那么你也可以采用添加自定义索引的方法。PostgreSQL内核已经包括了一套数量非常可观的索引类型,同时在内核之外,也已开发出了很多其他索引。
PostgreSQL官方收录的最新索引方法是KNN(K邻近域)——一个智能的索引,它可以返回K行值,而这些值可以按照离搜索目标距离大小进行排序。KNN的一个应用是模糊文本搜索,这里KNN用来对全文检索的结果进行排序,排序的依据是它们对检索项的匹配程度。在KNN之前,这种事是通过查询所有行记录来完成的,这个过程首先判断哪行数据匹配得更好,然后通过距离函数来排序并返回K个最佳值作为最终的结果。
如果使用KNN索引,这个索引的访问可以从按照期望的顺序返回行记录开始;所以这时候一个简单的LIMIT K函数将为你返回K个最佳匹配项。
也可以用KNN来计算真实的距离,比如,可以回答这样的问题“告诉我离中央火车站最近的10家比萨店”。
正如你所见,索引类型与它们指示的数据类型本身是相互分离的。又如,与整型数组的指标元素一样的GIN(通用倒排索引)可以用在全文检索中(同词干算法、词库和其他文本处理方法一起使用)。