不要在单个函数中验证和格式化

我想就用户输入的验证和格式化提出一条建议,尽管我从未见过有人提出过这样的建议。我想它会属于单一责任原则,所以对某些人来说这似乎很明显。这至少在当时看起来是个好主意,这可能是有原因的。

本质上,如果您想验证某些内容是否正确,请不要同时对其进行格式化。这两个操作应该在单独的函数甚至类中完成。我希望证明使用单个函数验证和格式化任何东西都是一个坏主意。我将主要使用 PHP 来演示这一点,但原则在任何语言中都应该几乎相同。

取以下名为 的函数isValid()。这是一个任意且简单的示例,但显示了在单个函数中使用的验证和格式设置。

function isValid(&$string) {
  $string = trim($string);
  if (strlen($string) < 8) {
    return false;
  }
  $string = strtoupper($string);
  return true;
}

传递给这个函数的 $string 参数是通过引用传递的,因此我们在整个函数中对字符串所做的任何事情都会改变传递给它的原始字符串。如果字符串长度小于 8 个字符,则该函数返回 false。否则字符串为大写,函数返回真。在任何一种情况下,字符串都会被修剪,因此很有可能在完成时字符串总是不同的。

使用上面的函数,我们可能会实现这样的东西。

$string = 'abcdefghijk  ';
if (isValid($string) == true) {
  echo 'string is valid';
}
else {
  echo 'string is invalid';
}

在此函数完成后,我们的字符串现在被修剪并为大写,但没有迹象表明这确实发生了。这可能会令人困惑。

我能理解为什么这种事情似乎是正确的做法。从表面上看,如果我们已经验证了我们的字符串,那么它也应该被格式化似乎是有道理的。事实上,验证代码自然会包含许多与格式化代码相同的精细细节,因此它可以让人感觉做正确的事情。

不幸的是,使用验证和格式化字符串的单个函数,我们可能会陷入使用验证函数格式化字符串而不实际执行任何验证的情况。这就是像这样令人困惑的代码开始发生的地方。

$string = getEnteredString();
 
isValid($string);
 
saveStringToDatabase($string);

在这个例子中,一个被调用的函数getEnteredString()用于在将它保存到数据库之前获取一些预先验证的用户输入的字符串。在此之前,有一个对 的调用isValid(),它仅用于格式化字符串。这看起来不正确,您需要仔细查看isValid()以找出在这种情况下使用它的原因。不熟悉代码库的人会认为这是一个错误并开始修复一个不存在的错误。我很欣赏这个函数可能不会被调用isValid(),但是对于这个函数在这种情况下应该做什么仍然令人困惑。

解决此问题的最佳方法是从isValid()函数中删除所有格式化代码,并将其仅用于验证。

function isValid($string) {
  if (strlen(trim($string)) < 8) {
    return false;
  }
  return true;
}

然后,在一个单独的函数中,我们可以格式化字符串。

function formatString($string) {
  return strtoupper(trim($string));
}

然后可以像这样使用这两个函数。

if (isValid($string) == true) {
  echo 'string is valid';
  $string = formatString($string);
}
else {
  echo 'string is invalid';
}

在此示例中,如果验证通过,则可以单独格式化字符串。

虽然它可能看起来像我们已经写了代码在这里的关注点分离,使两者的测试和维护变得更加容易。能够根据已知输入准确预测函数的确切输出意味着测试可以更简单地执行。

如果所有东西都放在同一个函数中,那么调整验证就会变得更加复杂,因为格式化部分也需要考虑和验证。我遇到过这种情况几次,尝试更改验证和格式化功能变得非常棘手。您可能会发现自己处于这样一种情况:您有一个庞大的单一功能试图完成所有工作,并且其复杂性可能很难理解。