在我的经验中,在进入分支和等效逻辑表达式之前,将功能分解(将事物分解为执行一项任务并执行良好的较小函数)几乎总是更有效率的。
引用其他方面,功能分解的行为将导致您了解代码的作用,从而导致进一步的分解机会。您想获得只需少量参数并返回无个、一个或少量值的函数。
通过分解,您将更好地了解哪些部分在算法上是必要的(必须以正确的方式完成)以及哪些部分仅仅是实现细节(必须完成,但可以以各种不同的方式完成而不改变输出)。
通常,您会发现许多深嵌套的`if`语句是不必要的,或者是重复冗余的,或者足够相似,如果您因子出代码中的微小区别,则可以将它们缩减为一个或几个函数。
换句话说,您想掌握的不是代码的细枝末节,而是代码所处理的抽象内容。许多问题归结为某些数据结构(列表、队列、树、集合)和对该结构的某些操作。在您能够分离数据结构和算法的程度上,您可以获得简化事物的抽象级别。或者您可以发现无论您必须做什么都更适合其他结构或算法,然后您可以消除许多不必要的东西。
多年前,我记得有个同事总是编写数据库游标,做任何事情,因为那是他知道如何做的全部。设置和撤销游标需要一些样板,以及带有某些错误检查的循环,因此他的代码始终看起来表面上很复杂。他会在存储过程中执行它,这当然增加了更多的样板。
现在,正如所发生的那样,这是在Sybase T-SQL中完成的,它具有一个全局错误变量和一个光标状态全局变量,他会两次检查它们,一次当他得到第一个光标行时,然后在遍历所有其他行的循环中再检查一次。他经常混淆错误变量(@@error
)和光标状态变量(@@sqlstatus
,在成功时为零(获得光标行),失败时为1,在光标中没有更多行时为2)。他的代码在名义上的路径上可以工作,因为如果他已经得到了一行,则两者都为零,当他试图获取最后一行后的行时,他会得到一个错误。因此,如果你仔细看他的代码,你会叹气(再一次!),并替他修正。
但是,接下来你会更仔细地观察他的代码,并发现他正在像游标一样浏览x=1
的所有行集,并且对于每一行设置y=y*2
,你最终会告诉他:"只需编写一个更新语句!"。
你纠正他检查全局变量的错误虽然是正确的,但并没有解决实际问题,即他没有充分理由使用游标和存储过程,而只需从客户端代码发送一个更新语句。
找出这个问题更加困难,因为你不仅需要查看全局变量的本地使用情况,还需要查看两个地方:光标声明的位置(declare cursor_foo cursor for select * from table where x = 1 for update;
)以及20行之后更新发生的位置(update table set y = y * 2 where current of cursor_foo
)。(所有这些都将是多行的,并且非常模板化。)
确定问题所在更加困难,因为你假设没有人会花费这么大的力气来进行一次更新;当然所有这些样板代码必须是因为更新中发生了特殊的事情,对吗?还是因为where
谓词是动态的或其他原因?因此,你会看到这个,作为一个尊重同事的谦虚程序员,你的第一反应会是,“我在这里错过了什么,肯定有一个使用光标的理由?”
(尽管我和他的老板都解释了@@error和@@sqlstatus之间的区别,但他从来没有真正理解,更不用说他几乎总是可以只进行一个update
了;他以过程化的代码思考,从未“懂得”数据库,尽管自称为“数据库程序员”。)
教训是在确认需要细节(实现细节)之前不要陷入细节中。首先分解你的代码,你就有更好的机会理解你所处理的抽象,并真正改进代码。