关于编程原则,实际上并没有一套统一的原则可以适用于所有的系统,不同应用场景、不同架构的系统,对于编程原则的要求相差还是比较大,看看以下著名的17条UNIX编程原则和NASA 的10条安全编码准则,大家就可想而知了。当然,两者并非对比,而是侧重点不同。
1、模块化原则(Rule of Modularity)
开发人员应该使用定义良好的界面连接简单的部分来构建程序,所以问题是本地的,部分程序可以在未来的版本中替换以支持新的功能。此规则旨在节省调试复杂,长期且不可读的代码的时间。
2、清晰原则(Rule of Clarity)
开发人员应该编写清晰的程序,就好像最重要的沟通是向开发人员读取和维护程序,而不是计算机。这个规则的目的是使代码在将来的代码中尽可能易读和易理解。
3、和解原则(Rule of Composition)
开发人员应该编写能够与其他程序轻松通信的程序。这条规则的目的是让开发人员把项目分解成小而简单的程序,而不是过于复杂的单片程序。
4、分离规则(Rule of Separation)
开发者应该将程序的机制与程序的策略分开;一种方法是将程序分成与该接口通信的前端接口和后端引擎。这条规则旨在通过允许改变策略,尽可能降低操作机制的不稳定性来防止错误引入。
5、简单规则(Rule of Simplicity),
开发人员应该设计简单的方法,通过寻找方法将程序系统分解成小而直接的合作件。这条规则的目的是阻止开发者写作“复杂而美丽的复杂性”,这是现实中容易出错的程序。
6、简约规则(Rule of Parsimony)
开发人员应该避免编写大型程序。这一规则的目的是防止由于项目的所有者不愿抛弃显着的大量工作而导致失败或次优方法的过度投资。较小的程序不仅易于编写,优化和维护,弃用时更容易删除。
7、透明度原则(Rule of Transparency)
开发人员应该设计可见性和可发现性,通过编写这样一种方式,他们的思维过程可以清楚地被未来的项目开发人员所看到,并使用输入和输出格式,以便识别有效输入和正确输出。此规则旨在减少调试时间并延长程序的使用寿命。
8、稳健性规则(Rule of Robustness)
开发人员应该通过设计透明和可发现性来设计强大的程序,因为易于理解的代码更容易对复杂程序中无法预见的意外情况进行压力测试。此规则旨在帮助开发人员构建强大,可靠的产品。
9、表示规则(Rule of Representation)
开发人员在面对选择时应该选择使数据更复杂,而不是程序的逻辑,因为与复杂的逻辑相比,人类更容易理解复杂的数据。这条规则的目的是使任何开发项目的开发人员都可以使程序更易读,从而使程序得以维护。
10、最小惊喜规则(Rule of Least Surprise)
开发人员应该根据潜在用户的预期知识设计程序。例如,计算器程序中的“+”应该总是指“加法”。该规则旨在鼓励开发人员构建易于使用的直观产品。
11、沉默的规则(Rule of Silence)
开发人员应该设计程序,以免打印不必要的输出。这个规则旨在允许其他程序和开发者从程序的输出中挑出他们需要的信息,而不必分析冗长。
12、修理规则(Rule of Repair)
开发人员应该设计失败的程序,易于本地化和诊断,换句话说就是“失败”。这条规则旨在防止程序的错误输出成为输入,并破坏未被检测到的其他代码的输出。
13、经济规则(Rule of Economy)
开发人员应该重视开发人员在机器上的时间,因为与上世纪70年代的价格相比,今天的机器周期相对便宜。这条规则旨在降低项目的开发成本。
14、生成规则(Rule of Generation)
开发人员应该避免手动编写代码,而是编写抽象的高级程序来生成代码。此规则旨在减少人为错误并节省时间。
15、优化规则(Rule of Optimization)
开发人员应该在打磨软件之前制作原型。这条规则旨在防止开发者花费太多时间来获得边际收益。
16、规则的多样性(Rule of Diversity)
开发者应该设计他们的程序是灵活的,开放的。这条规则的目的是使程序更加灵活,使其能够以开发者所期望的方式使用。
17、可扩展性规则(Rule of Extensibility)
开发人员应该通过使其协议可扩展来设计未来,允许轻松插件,而无需修改其他开发人员的程序架构。
由NASA的首席科学家Gerard J. Holzmann制定
1、简化控制流程
使用尽可能精简的控制流程设计: 不要使用 setjmp 或 longjmp、goto 语句,以及直接或间接的递归调用。
2、使用固定的循环次数上限
所有的循环必须有一个固定的上限。 必须可以被检测工具静态地证实,该循环的迭代器不会超出上限值,如果不能被静态地证实,则可以认为违背该规则。
3、不使用动态内存分配
不要在初始化完成后进行动态内存分配。
4、不使用冗长的函数
要求函数的代码长度按照每个声明占一行、每个语句占一行这样的标准参考格式,能在一页纸上打印出来。这意味着函数的代码不应超过60行。
5、低断言密度
代码中的断言密度(assertion density)应低至平均每个函数2个断言。断言用于检查实际执行过程中绝不应出现的异常状况,必须定义为Boolean测试。当断言失败时,应执行明确的恢复操作。如果静态检查工具发现断言永远不可能失败或永远不会被触发,则可认为未遵守该原则。
6、以最小范围级别声明数据对象
该原则同时也是数据隐蔽(Data hiding)的基本原则。所有数据对象必须以尽可能小的范围级别进行声明。
7、检查参数和返回值
应在每次调用函数后检查非void函数的返回值,并在每个函数内部检查参数的有效性。
8、限制预处理程序的使用
预处理程序(Preprocessor)只能在头文件和宏定义中使用。递归的宏调用、符号拼接,以及可变参数列表均不允许使用。
通常不建议使用条件编译指令,如果代码中不能够避免时,必须有基于工具的检查器进行标记,并有充足的理由。
9、限制指针的使用
必须限制指针的使用,不允许有超过一级的指针解引用操作。指针解引用操作不能隐藏在typedef类型声明或宏定义中。不允许使用函数指针。
10、编译所有代码
从开发工作第一天开始,就必须对所有代码进行编译。必须启用编译器的警告功能,并使用最细致的检查选项。在此设置之下,代码必须零警告编译通过。代码必须使用源代码静态分析工具,每天检查一次以上,且零警告通过。