程序员修炼之道-从小工到专家(摘录二)元程序设计

2018/01 08 22:01

再多的天才,也无法胜过对细节的专注。
–Levy’s Eighth Law

细节会弄乱我们整洁的代码–特别是如果它们经常变化。每当我们必须去改动代码,以适应商业逻辑、法律或者管理人员个人一时的口味的某种变化时,我们都有破坏系统–或引入新bug–的危险。
所以我们说“把细节赶出去!”把它们赶出代码。当我们在与它作斗争时,我们可以让我们的代码变得高度可配置和“软和”–也就是,容易适应变化。

动态配置

首先,我们想要让我们的系统变得高度可配置。不仅是像屏幕颜色和提示文本这样的事物,而且也包括诸如算法、数据库产品、中间件技术和用户界面风格之类更深层面的选择。这些选择应该作为配置选项、而不是通过集成或工程(engineering)实现。
Configure,Don’t Integrate – 要配置,不要集成
要用元数据(metadata)描述应用的配置选项:调谐参数、用户偏好(user preference)、安装目录,等等。
元数据到底是什么?严格地说,元数据是关于数据的数据。最为常见的例子可能是数据库schema或数词典。schema含有按照名称、存储长度及其它属性、对字段(列)进行描述的数据。你应该能访问和操纵这些信息,就像对数据库中的任何其它数据一样。
我们在其最宽泛的意义上使用术语。元数据是任何对应用进行描述的数据–应用该怎样运行、它应该使用什么资源,等等。在典型情况下,元数据在运行时、而不是编辑时被访问和使用。你每时每刻都在使用元数据–至少你的程序是这样。假定你点击某个选项,隐藏你的web浏览器上的工具栏,浏览器将把该偏好作为元数据存储在某种内部数据库中。
这个数据库可以使用私有格式,也可以使用标准机制。在Windows下,初始化文件(使用后缀.ini)或系统注册表中的条目都很典型。在Unix下,X Windows System使用Applicatioin Default文本提供类似的功能。Java 使用的是Property文件。在所有这些环境中,你通过指定关键字来获取值。另外,更强大和灵活的元数据实现会使用嵌入式脚本语言。
Netscape浏览器实际上使用了这两种技术实现偏好。

元数据驱动的应用

但我们不只是想把元数据用于简单的偏好。我们想要尽可能多地通过元数据配置和驱动应用。我们的目标是以声明方式思考(规定要做什么,而不是怎么做),并创建高度灵活和可适应的程序。我们通过采用一条一般准则来做到这一点:为一般情况编写程序,把具体情况放在别处–在编译的代码库之外。

Put Abstractions in Code,Details in Metadata
将抽象放进代码,细节放进元数据

这种方法有若干好处:
它迫使你解除你的设计耦合,从而带来更灵活、可应用性更好的程序。
它迫使你通过推迟细节处理,创建更健壮、更抽象的设计–完全推迟到程序之外。
无需重新编译应用,你就可以对其进行定制。你还可以利用这一层面的定制,轻松地绕开正在运行的产品系统中的重大bug。
与通用的编程语言的情况相比,可以通过一种大为接近问题领域的方式表示元数据。
你甚至还可以用相同的应用引擎–但是用不同的元数据–实现若干不同的项目。

我们想要推迟大多数细节的定义,直至最后时刻,并且尽可能让细节保持“软和”–尽可能易于改动。通过精心制作允许我们快速作出变更的解决方案,我们将能够更好地应付使许多项目覆没的”转向“(directional shift)。

商业逻辑

于是你让数据库引擎的选择变成了配置选项,并提供了元数据来确定用户界面风格。我们还能做得更多吗?肯定能。
因为与项目的其它方面相比,商业政策与规则更有可能发生变化,以一种非常灵活的格式维护它们很有意义。
例如,你的采购应用可能包括了各种企业政策。也许你在45日内向小供应商付款,在90日内向大供应商付款。让供应商类型的定义以及时间周期自身,成为可配置的。抓住机会实行一般化。
也许你在编写一个具有可怕的工作流需求的系统。动作会根据复杂的(和变化的)商业规则启动和停止。考虑在某种基于规则的系统(即专家系统)中对它们进行编码,并嵌入到你的应用中。这样,你将通过编写规则、而不是修改代码来配置它。
对于不那么复杂的逻辑,可以使用小型语言加以表达,从而消除在环境变化时重新编译和重新部署的需要。