题
我正在和一位同事讨论你的代码应该具有怎样的防御性。我完全支持防御性编程,但你必须知道在哪里停止。我们正在开发一个将由其他人维护的项目,但这并不意味着我们必须检查开发人员可以做的所有疯狂的事情。当然,您可以这样做,但这会给您的代码增加很大的开销。
你怎么知道在哪里划清界限?
解决方案
我不知道有真的没有办法回答这个问题。这只是你从经验中学习的东西。你只需要问自己一个潜在的问题是如何共同可能是并作出判断。同时认为,你不一定的的的始终代码防守。有时,它是可以接受的只是要注意你的代码的文档中发现任何潜在的问题。
虽然最终,我认为这只是一些人必须遵循他们的直觉。有这样做没有正确或错误的方式。
其他提示
任何一个用户进入直接或间接地,应该始终强>完整性检查。除此之外,这里的几个assert
s,所以才没有受伤,但你真的不能做太多关于疯狂的程序员修改和破坏你的代码,反正 - !)
我倾向于改变我把我的代码基于语言防守的量。今天我主要是用C ++的工作,所以我的想法是在这个方向漂移。
用C工作时++不能有足够的防御性编程。我把我的代码,如果我守着核机密和所有其他的程序员就是要控制他们。断言,抛出,编译时错误模板黑客,参数验证,消除指针,深入的代码审查和一般的偏执都是公平的游戏。 C ++是一个邪恶的美妙语言,我又爱又严重不信任。
我不是术语“防御式编程”的粉丝。到我它表明这样的代码:
void MakePayment( Account * a, const Payment * p ) {
if ( a == 0 || p == 0 ) {
return;
}
// payment logic here
}
这是错,错,错的,但我必须看到它几百倍。该函数不应该被称为在第一位置空指针,这是完全错误的悄然接受。
这里正确的做法是有争议的,但最小的解决办法是吵闹失败,或者通过使用一个断言或抛出异常。
编辑:我不同意一些其他的答案和评论在这里 - 我不认为所有的功能应该检查其参数(很多功能,这是根本不可能的)。相反,我认为,所有的功能应该记录这是可以接受的状态,其他值将导致不确定的行为的价值观念。这是有史以来最成功并广泛使用的库所采取的方法 - 的C和C ++标准库
现在让downvotes开始...
如果你工作在一个组件的公共API那么它值得做参数验证一个良好的数额。这使我有到处做确认的习惯。那是一个错误。所有这些验证代码永远不会被测试并且可能使系统复杂得多它需要。
现在我更喜欢由单元测试,以验证。验证肯定会发生数据从外部源的到来,而不是来自非外部开发者的呼叫。
我总是Debug.Assert的我的假设。
我的个人思想:一个程序的防御应该正比于潜在用户群的最大幼稚/无知
防御开发人员使用您的 API 代码与防御普通用户没有什么不同。
- 检查参数以确保它们在适当的范围内并且属于预期类型
- 验证可进行的 API 调用次数是否在您的服务条款范围内。一般称为节流,通常仅适用于 Web 服务和密码检查功能。
除此之外,除了确保您的应用程序在出现问题时能够很好地恢复并且始终向开发人员提供充足的信息以便他们了解发生了什么之外,没有什么其他可做的。
防御性编程只是履行合同的一种方式 按合同设计 编码方式。
另外两个是
- 总体规划 和
- 名义编程。
当然你不应该为自己辩护 每一件疯狂的事情 开发人员可以这样做,但是您应该在上下文中声明它将使用先决条件执行预期的操作。
//precondition : par is so and so and so
function doSth(par)
{
debug.assert(par is so and so and so )
//dostuf with par
return result
}
我认为你必须在你是否正在创建测试以及问题带来。你应该在你的编码防守,但指出了JaredPar - 我也相信这取决于你使用的语言。如果它的非托管代码,那么你应该非常抗跌。如果它的管理,我相信你有wiggleroom一点点。
如果您有测试,以及其他一些开发商试图消灭你的代码,测试将失败。但话又说回来,这取决于测试覆盖在你的代码(如果有的话)。
我尝试写的代码比防守更,但降权敌意。如果出现问题,我能解决这个问题,我会的。如果不成功,就抛出或通过异常并使其别人的问题。凡是与物理设备交互 - 文件系统,数据库连接,网络连接,应考虑unereliable和容易出现故障。预期这些故障和陷井他们是关键
一旦你有了这种心态,关键是要在你的方法是一致的。你希望交还状态代码comminicate问题在调用链或做你喜欢的异常。混合模型会杀了你,或者至少送你去喝酒。巨资。如果您使用的是别人的API,那么这些东西隔离到您使用的陷阱/报告方面的机制。使用这些包裹接口。
如果在这里讨论的是如何应对未来的防守码(可能是恶意的或不称职)的维护者,还有就是你可以做什么限制。通过测试覆盖率和自由使用断言你的假设执行合同可能是你能做的最好的,它应该在理想情况下没有混乱的代码,使工作更加困难的未来为非作恶维护的方式来完成码。断言易于阅读和理解,并明确给定一段代码的假设是什么,所以他们通常是一个好主意。
防守编码对用户的动作完全是另外一个问题,那我用的是认为用户是出去找我的做法。将每个输入尽可能仔细我可以管理检查,我尽一切努力,使我的代码失效安全 - 尽量不要坚持未严格的审查,任何状态下,正确的地方就可以了,退出优雅如果你不能等,如果你想想,可能对你的代码被外界剂犯下的所有博佐的东西,它可以让你在正确的心态。
编码防守对其他代码,比如你的平台或其他模块,是完全一样的用户:他们出去找你。操作系统总是会在不合适的时候,您可以切换线程,网络总是会在错误的时间去了,和一般的,邪恶的每个角落比比皆是。你不需要编写针对每一个潜在的问题在那里 - 在维护的成本可能不值得增加安全性 - 但它肯定不会伤害去想它。它通常不伤害在代码中明确评论,如果有你想过的场景,但等闲视之出于某种原因。
系统应该有精心设计的界限在那里防守检查情况。应该有什么地方用户输入验证程序员的不同团队编码的决定(在什么边界)和到其他潜在的防守问题需要检查(例如,第三方集成点,公开可用的API,规则引擎的相互作用,或不同的单位)。更多的防守比检查违反DRY在许多情况下,只是增加了维护成本非常小中获益,
话虽这么说,有一定的点,你不能太偏执。潜在的缓冲区溢出,数据损坏和类似的问题应该是非常严格的要防范。
我最近有场景,其中用户输入数据通过远程门面接口传播,然后本地门面接口,则一些其它的类,以最终得到的地方,它实际上使用的方法。我问我自己一个问题:时,应值验证的我加验证码只到最后类,在实际使用的价值。添加在课堂上铺设传播路径上其他验证代码段将太防御性编程我。一个例外可能是远程门面,但我跳过它。
好问题,我已经做翻转的完整性检查,并没有做它们之间以失败告终。其50/50
的情况下,我可能会采取一个中间地带,我只会“防弹”是任何套路:
(a)中的项目从一个以上的地方叫
(b)的逻辑很可能会改变
(c)中,不能使用默认值
(d)的程序不能被 '失败' 正常
Darknight