由于业务的复杂多变,我们编写完的程序,在后期肯定要被修改,而且修改的人很可能不是自己。这种情况我们都遇到过。
而且,看别人的代码可能会觉得很痛苦:为什么他要这样写相关逻辑?为什么变量名称要这样定义?换用这种方式不是更好吗?……等等。原因很简单:我们没有一个相关的代码编写规范。而你所换用的方式可能对其他人不适合。
我们已经做成功了几个系统。而后期,我们还会编写或者维护更多的代码。这样,编码的规范性就显得很重要了。
下面是我遵循的一些规则,以及Steven的几个建议,欢迎拍砖。
1. 严格遵循命名约定,好像它们就是你的生命支柱。
我们可能有自己的命名方法,也许就是v_aab001, i, count, bj, jjzj之类的。你有没有发现阅读别人代码时的那个费劲?怎么变量命名方式和我的不一样呢?如果自己维护自己的代码,那多省事!
我们需要有一个规范,给别人方便,也给自己方便。下面是我定义变量的规范:
* 表字段相关变量的定义规则:<类型名>_<字段名>_<字段注释>
类型名:varchar2类型为“v”,number类型为“n”,date类型为“d”……
字段名:表中字段的名称。例如“aab001”、“aac002”……
字段注释:该字段的注释
* 普通变量的定义规则:<类型名>_<注释>
* 记录类型的定义规则:record_<记录注释>
* 记录变量的定义规则:rec_<记录注释>
* 类型的定义规则:type_<类型注释>
* 类型变量的定义规则:t_<类型注释>
具体使用什么规则的变量定义,需要你的项目组内各个成员的讨论。因为最终确定的变量定义规范,是需要大家主动遵守的,而不是被动的。
2. 格式化你的代码。
内层代码要空两格。空两格的优点是:当层次发生变化时,我可以使用“TAB”键或“SHIFT+TAB”组合键将选择的代码块前移两格或后移两格,来改变代码块的层次。
我们看看下面的代码格式。不用关心这段代码究竟实现了什么,注意看的是它内层与外层的逻辑关系是通过空格来直观表现的。
3. 编写规范和健壮的sql。
3.1 规范的sql
我们应该知道“绑定变量”的概念。要实现绑定变量,首要的一点是两个sql的格式统一。我们看看这样的sql格式是否必要:关键字对齐。
3.2 健壮的sql
而且,要知道每个sql都有可能出错的。所以我们需要使用异常捕获语句包裹每个sql,并给出恰当的提示。比如某系统前段时间一直有no_data_found的错误,程序抛出的异常就是这个。很简单,这说明我们的程序中有个select语句查询结果为空,但并没有捕获这个异常,所以直接抛出了no_data_found。这个错误是无法定位的。
每个编写的sql应该是类似这样的:
Begin
<Sql statement>;
Exception
When <错误1> then
<错误处理>;
When <错误2> then
<错误处理>;
……
When others then
<其他错误的捕获>
End;
这样,这个sql就在我们的控制之中了,如果提示信息足够准确,很容易定位到出错的位置。
下面是常用的三个sql异常捕获:
sql类型 | 相关错误 | 异常捕获 |
insert | 主键冲突 | dup_val_on_index |
select | 未查询到数据 | no_data_found |
查询到多行 | too_many_rows |
我工作两年多来,还没遇到过使用其他异常捕获的情况。除此以外,还有我们不可预期的错误,需要最后使用“others”来捕获。
关于异常的详细介绍,以及如何手工抛出指定异常,参考《oracle pl/sql programming》第六章。
4. 使执行部分短小:告别"意大利面条式的代码"。
以我们的过程“prc_p_bj”为例。其所在的包一直报“pls_00123:程序太大”,其实是因为该过程的“意大利面条式的代码”。该过程有2000多行,嵌套了若干次。更不幸的是,pl/sql编译器并不能识别这么多的嵌套。
详细介绍可参考:PLS-00123:程序太大
稍微分析一下,就发现这个过程包括三大部分:A、B、C。这是三个相互独立的业务,可以分成三个子过程来调用。这样就省去了一层嵌套。
再分析每个子过程,可以发现每个都包括了五个小部分的分别实现。这又可以拆分成五个子过程。又省去了一层嵌套。
拆分后的代码框架图见PLS-00123:程序太大。这样拆分后,消除了“程序太大”的错误。更重要的是,它易于代码的阅读、定位和维护。我要修改B部分,只要看原来1/3的代码;我要修改B的第三个小部分,只要看原来1/15的代码!
当然,这样直接拆分后的过程还不完美,因为它是紧耦合的。我们可以看看第五个建议。
5. 保持松耦合。
紧耦合与松耦合的概念很容易理解:我要修改一段代码,而且我知道修改这段代码带来的影响只是某个或某几个过程,这就是松耦合;反之,我发现了这段代码中的问题,但却不能确定修改后影响的是那几个过程,就很可能就是紧耦合了。
对于后台包,我们要力求做到该包中的过程只被该包中的某个或某几个过程来调用,来达到松耦合(公共包除外):
* 能定义为包体的局部过程或局部变量的,就不要在包中定义;
* 能在过程内定义的子过程或变量,就不要定义为包体中的局部过程或局部变量;
* 子过程使用的变量,不要在父过程中定义。
6. 定义明确的注释及注释层次。
6.1 明确的注释
我们写了一个insert语句,并且添加注释“向表x中插入数据”。这类注释可以去掉,因为阅读代码的人肯定懂insert语句,通过代码就知道是“向表x中插入数据”。代码其实也是注释的一种。
我们的注释,要注明该插入的前因后果,注明业务上的关系。比如“保存该人员基本信息”就比“向表ac01中插入记录”要明了得多。
6.2 注释的层次
代码之间是有层次的,而注释就要体现这层关系。这样,通过阅读注释,就知道代码的结构了。下面是一个小例子:
--0.初始化
<代码>
--1.计算金额A,B,C
<代码>
--1.1初始化
<代码>
--1.2计算金额A
<代码>
-- 1.2.1计算金额A的步骤1
<代码>
--1.2.2计算金额A的步骤2
<代码>
--1.2.3计算金额A的步骤3
<代码>
--1.3计算金额B
<代码>
--1.4计算金额C
<代码>
--2.计算金额D
<代码>
--3.计算金额E
<代码>
--4.保存该人员的XX信息
<代码>
--5.结束:设置标志,提交等
<代码>
7. 找一位伙伴:非常赞同找个人来监督你的工作。
这个建议Steven讲的很透彻:
计算机并不会编程,人才会。
有多少次你弯着腰、驼着背坐在计算机前,因无法找出代码中的错误而感到非常郁闷?先是几分钟过去了,接着又过了几小时。最后,对自己都厌烦了,感到非常失败,你把头伸出你的小隔间并请朋友过来帮你看一看。
通常会有下面三种情况之一出现:
* 当你的朋友从她的椅子上站起来时,一切都在瞬间变得非常清楚。
* 你的朋友瞥了一眼屏幕,马上就指出了问题所在。
* 你的朋友不负责该系统中你所做的部分,所以你必须说明你的程序在干什么。当你逐步讲解逻辑时,引起错误的问题所在会突然暴露在你面前。
事实就是自己很难调试自己的代码,因为你自己对它太投入、太专注了。
这个问题最好的解决办法是由开发经理创造这样一种文化:各种想法是共享的、不懂是可以原谅的并不会受到处罚、定期进行建设性的代码评审。不幸的是,这些文化上的改变是难以实现的。
与此同时,我建议在帮助改变你所在小组的文化的过程中你应起带头作用。找到另一位开发人员,最好比你经验丰富,并建立一种"伙伴"关系:在出现问题时,他可以充当你的参谋,当然,你也可以充当他的参谋。事前达成共识:不知道所有问题的答案并没有什么不对。
然后为你自己制定一条简单的规则:不要为一个错误苦思冥想超过半个小时。30分钟过去后,把你的伙伴叫过来,让人类心理学为你服务,而不是跟你作对