什么是防御性编程
维基百科中对防御性编程有如下定义:
防御性编程(Defensive programming)是防御式设计的一种具体体现,它是为了保证,对程序的不可预见的使用,不会造成程序功能上的损坏。它可以被看作是为了减少或消除墨菲定律效力的想法。防御式编程主要用于可能被滥用,恶作剧或无意地造成灾难性影响的程序上
我们进行防御性编程的目的主要在于提高程序的健壮性,防止从程序外部传入的非法数据造成程序崩溃。
例如下面一个简单的遍历二叉树的程序:
void Preorder_Recursive(TreeNode * root)
{
cout << root->val <<" "; //操作根节点
if(root->left)
{
Preorder_Recursive(root->left); //转向左子树
}
if(root->right)
{
Preorder_Recursive(root->right); //转向右子树
}
}
如果此时传入的root
是一个空指针,此时程序就会立即崩溃。因此,此时我们需要加入相应安全性检查的代码
//加入安全性检查的代码
void Preorder_Recursive(TreeNode * root)
{
if(!root)
{
return;
}
cout << root->val <<" "; //操作根节点
if(root->left)
{
Preorder_Recursive(root->left); //转向左子树
}
if(root->right)
{
Preorder_Recursive(root->right); //转向右子树
}
}
此时,再次传入一个空指针也不会造成程序崩溃了。
当然,我们的程序的首要任务还是要保证正确性。防御性编程是为了在正确性的基础上增强程序的健壮性或在正确性与健壮性之间取得一定的折中。
怎么做
既然要进行防御性编程,那么我们又该采取哪些措施来进行防御性编程呢?下面是我总结的一些手段。
不要相信任何人
不要相信任何人传入到程序中的数据,甚至是你自己。在编写程序的任何时刻牢记对获得的数据进行检查,判断其是否有可能是非法数据的情况。如果有,又会有哪些是非法的,又该怎样进行处理。想清楚再动手写代码
在写代码之前请先在大脑中考虑清楚程序有哪些输入,又有哪些非法输入,程序如何执行,正常输出是什么,对待非法输入如何处理。请一定先在大脑中或借助纸和笔考虑清楚再动手写代码。尽量少写代码
程序员的主要目的是解决问题而不是写代码,写代码只是解决问题的一种手段。在考虑清楚的情形下,能不写代码尽量不写代码,能少些代码尽量少写代码。写更多的代码意味着更多潜在的错误。拥有良好的编程风格
尽力养成良好的编程风格。好的编程风格能够体现作者清晰的思路并且令代码清新易懂。尽力减少重复代码并保证一个函数只做一件事。善于运用设计模式使各个函数之间高内聚且低耦合,方便代码修改且不容易犯错。时刻谨记,清晰易懂的代码胜过聪明的代码。尽量隐藏
利用好oop编程的封装特性,使用公共的访问方法代替属性访问。除非必要,尽量不把属性设为public
。利用好编程语言的异常处理机制
请尽量使用编程语言提供的异常处理机制,在相应的位置设置合适的异常捕获代码并进行适当的异常处理。对错误的视而不见则意味着置程序于危险。善用IDE
现代的IDE都够帮助我们检查出程序中潜在的错误,如类型转换、没写case
的default
语句、函数不能够总是有返回值、死循环等等情况。同时,IDE也可以帮助你更好的理解代码并帮助你改善你的代码,所以,请抛弃简陋的文本编辑器而使用专业的IDE。尽量使用安全的数据结构
能用安全的数据结构代替原始的不安全的数据结构的时候请一定使用安全的数据结构。比如,JAVA
或C++
中尽量使用ArrayList
或vector
代替原始的数组。这些容器结构已经设计的足够高效和安全,用来替代原始的数组可以保证足够的效率和安全性。使用合适的数据类型
能用int
的时候请不要使用unsinged int
,能用double
时也请不要使用float
,尽量使用不容易造成数值溢出的数据类型。如果数值足够大以至于long long int
也不能够放得下的时候可以考虑用字符串表示或编写相应的数据结构。使用智能指针而不是原始指针
这一条主要用在c++
当中,请尽量使用能够自动释放的智能指针而不是原始指针。如果一定要使用原始指针,请一定要对指针保持足够的警惕,时刻考虑其是否在适当的时机进行释放或是否是一个空指针。声明时便初始化
变量声明时即应该进行初始化,以防止函数使用或返回未初始化的变量造成程序错误或崩溃。善用静态分析工具,可以尽早发现此类错误并进行改正。
总结
暂且想到这么多,等下次再想到哪些再来补充。
总之,能够编写正确且健壮的代码是非常重要也是非常苦难的。进行防御性编程就是能够提前预料到程序的危险并做好相应的准备措施。同时,防御性编程也会给我们养成一种良好的思维习惯和编程风格,使我们能够预知危险,未雨绸缪。