if-throw前提条件检查有效性和DRY原理

问题描述 投票:0回答:1

[许多互联网资源坚持通过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)检查!是不是太多了?即使以与上述相同的方式进行了优化,使用间接调用或长函数(可能不会内联)的这种情况又如何呢?

所以,我的程序应该按照以下规则编写吗?

  • [如果某个API函数可能不会被内联,或者被期望通过调用站点上的检查被调用很多次(请参见vector示例),请assert()其前提条件(在其中);
  • try-catch throwing函数,而不是在调用之前检查其先决条件(后者似乎破坏了DRY)。

如果没有,为什么?

c++ exception assert dry preconditions
1个回答
0
投票

因此,您要谈论的是两件事: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的函数,不需要检查它是否满足前提条件,因为只有在满足条件时才创建它。

© www.soinside.com 2019 - 2024. All rights reserved.