面条式代码
面条式代码(Spaghetti code)是软体工程中反面模式的一种[1],是指一个程式码的控制结构复杂、混乱而难以理解[2],尤其是用了很多GOTO、例外、执行绪、或其他无组织的分歧架构。其命名的原因是因为程式的流向就像一盘面一样的扭曲纠结。面条式代码的产生有许多原因,例如没有经验的程式设计师,及已经过长期频繁修改的复杂程式。结构化程式设计可避免面条式代码的出现。
举例
以下是一段用BASIC写的程式,是典型面条式代码的例子。程式在萤幕上显示数字1到10及其对应的平方。由于有GOTO
指令,此程式需要配合行号才能知道程式的流向,也无法利用缩排的方式使程式较容易阅读。而且因为跳跃指令的关系,要执行的程式会不可预测的由一个区域跳到另一个区域,不易追踪。现实世界中的面条式代码往往更加复杂,会大幅增加维护的成本。
10 i = 0
20 i = i + 1
30 PRINT i; " squared = "; i * i
40 IF i >= 10 THEN GOTO 60
50 GOTO 20
60 PRINT "Program Completed."
70 END
以下则是使用结构化的控制架构后的程式,由于没有GOTO
指令,程式已不需要行号,而且可以用缩排的方式,增加程式可读性:
Public Sub Main()
For i As Integer = 1 To 10
Console.WriteLine("{0} squared = {1}", i, i ^ 2)
Next
Console.WriteLine("Program Completed.")
End Sub
程式中还是有由一个区域跳到一个区域的情形,不过这种跳跃是可预期的,也是标准的作法。使用FOR回圈或函式是处理程式流程控制的标准作法。若使用GOTO,也就表示允许程式任意的跳跃。上述范例的程式码很短,实际使用的程式其程式码更长,若是面条式代码的话会相当难以维护。
组合语言及脚本语言
当使用各种组合语言(及其底层的机械码)时,撰写面条式代码会带来更大的危险。其原因是由于这些低阶语言很少有可以对应FOR回圈或WHILE回圈的机能。许多脚本语言也有类似的情形,例如DOS的批次档或是OpenVMS上的DCL。
若将结构化程式设计中的作法移植到组合语言的程式,会对可靠性及可维护性有显著的改善。例如限制GOTO的使用,只用GOTO来产生类似结构化程式设计中流程控制的效果、另外许多组合语言都有提供函式呼叫的机制,可以有类似程序化程式设计(Procedural programming)的效果。组合语言一般都会有巨集,而且支援参数传递,以避免全域变数的使用,也可避免远隔作用(action at a distance)的反面设计模式。
使用高阶语言撰写的程式可以利用一些标准流程控制的作法(如以上第2例的for loop),不过当组译为组合语言或机器码时,由于最后仍利用GOTO或IF之类的指令表示高阶语言的标准流程控制,看起来会像是面条式代码。因为组译器会忠实的将程式的结构转换为组合语言,因此不会遇到其他结构性较弱的语言所遇到,程式流程难以辨识的问题。不过,若是程式作了过多的最佳化,可能在缩小程式大小的同时,也影响其程式的结构,若配合 source-level debugger 使用,有时会因些造成一些困扰。
馄饨式代码
馄饨式代码(Ravioli code)是指程式中是由许多小的、松散连接的部份所构成。馄饨式代码可以和面条式代码作比较,后者用面条来代表程式的结构,而前者用馄饨(Ravioli)来代表程式中的物件。
参见
- 结构化程式设计 程式中不使用
goto
,只使用像 loop, for 及其他的流程控制指令。
参考文献
- ^ William J. Brown, Raphael C. Malveau, Hays W. "Skip" McCormick, Thomas J. Mowbray(1998). AntiPatterns: refactoring software, architectures, and projects in crisis. (1st ed.). Wiley. ISBN 0471197130.
- ^ J. Stanley Warford (2009). Computer Systems. (4th ed.). Jones & Bartlett Publishers. ISBN 0763771449.
本条目部分或全部内容出自以GFDL授权发布的《自由线上电脑词典》(FOLDOC)。