[许多互联网资源坚持通过if (something_is_wrong) throw Exception{}
而不是assert(!something_is_wrong)
来检查API函数中的前提条件,我发现其中有一些好处。但是,恐怕这种用法可能会使相同的检查加倍:
void foo(int positive) {
if (positive < 1) {
throw std::invalid_argument("param1 must be positive");
}
}
void caller() {
int number = get_number_somehow();
if (number >= 1) {
foo(number);
}
}
可能会执行类似
int number = get_number_somehow();
if (number >= 1) {
if (number < 1) {
throw std::invalid_argument("param must be positive");
}
}
除非我想通过切掉if
之一来实际上内联和优化呼叫,否则。此外,两次写入支票(在foo()
和caller()
中)可能违反了DRY规则。因此,也许我应该去
void caller() {
int number = get_number_somehow();
try {
foo(number);
} catch (std::invalid_argument const&) {
// handle or whatever
}
}
为了避免重复这些前提条件检查,在功能合同变更的情况下提供一些性能和很多可维护性。
但是,我不能总是采用这种逻辑。想象一下std::vector
仅具有at()
而没有operator[]
:
for (int i = 0; i < vector.size(); ++i) {
bar(vector.at(i)); // each index is checked twice: by the loop and by at()
}
此代码会导致额外的O(N)检查!是不是太多了?即使以与上述相同的方式进行了优化,使用间接调用或长函数(可能不会内联)的这种情况又如何呢?
所以,我的程序应该按照以下规则编写吗?
vector
示例),请assert()
其前提条件(在其中);如果没有,为什么?
因此,您要谈论的是两件事:DRY和性能。
DRY与代码的维护和结构有关,实际上不适用于您无法控制的代码。因此,如果该API是黑匣子,并且其中包含一些您无法更改的代码,但是您需要单独进行修改,那么我不会认为它不是DRY可以在您的代码中重复码。 Y是你自己。
但是,您仍然可以在意性能。如果您测量了性能问题,则请以合理的方式进行解决-即使它是防DRY的(如果可以,也可以)。
但是,如果您同时控制API和客户端这两个方面,而您确实想要一个纯净,无重复的高性能解决方案,那么就有一种类似于这种伪代码的模式。我不知道名字,但我认为它是“提供证明”
let fn = precondition_check(myNum)
if fn != nil {
// the existence of fn is proof that myNum meets preconditions
fn()
}
API func precondition_check
返回捕获其中的myNum
的函数,不需要检查它是否满足前提条件,因为只有在满足条件时才创建它。