标准化的返回值 - 这是一个好主意还是坏主意

时间:2012-03-06 08:20:43

标签: php

我正在使用PHP(但在这种情况下,我认为编程语言并不重要),在我的课程方法中,我通常会遇到以下情况:

  1. 方法必须返回 true false
  2. 方法必须返回 true 错误消息
  3. 方法必须返回 true + 成功消息 false + 错误消息
  4. 方法必须返回 true + 成功结果(对象,数组,等等) false
  5. 方法必须返回 true + 成功结果(对象,数组,等等) false + 错误消息
  6. 我的问题是,当我在我的代码中使用这个类方法时,我总是要回到类中,检查实际返回的方法是什么:只需 true false true 错误消息等。

    标准化返回值是个好主意吗?如果是,怎么样?

    我的想法是:

    1. 如果函数必须返回 true false ,则只需返回 true false
    2. 如果函数必须返回 true 错误消息,那么:

      if (success)
      {
          return array(
              TRUE,
              null
          );
      }
      else
      {
          return array(
              FALSE,
              $error_message
          );      
      }
      
    3. 如果函数必须返回 true + 成功消息错误消息,那么:

      if (success)
      {
          return array(
              TRUE,
              $success_message,
          );
      }
      else
      {
          return array(
              FALSE,
              $error_message
          );      
      }
      
    4. 我希望你能理解我的问题,甚至认为我的解释不太好:) 您有什么建议或最佳做法?我该怎么办呢?

      更新: 我们举一个简单的例子:

      function login($username, $password) 
      {
          // Login logic here ..
          if ($logged_in) 
          {
              return TRUE;
          }
          {
              return $error_message;
          }
      }
      

      所以正确的方法是:return true ,或抛出异常,并在调用login方法时执行 try catch < / em>的。因此,当出现问题(验证失败等)时,我应该使用例外。

7 个答案:

答案 0 :(得分:7)

我要说的是返回布尔其他东西的前提是错误的。

功能应该有明确的目的,结果清晰。如果可以实现此结果,则返回结果。如果无法实现结果,则函数返回false或抛出异常。哪个更好取决于情况和您的一般错误处理理念。无论哪种方式,让函数返回错误消息通常都没有用。该消息对调用该函数的代码没有用。

除了返回false结果外,PHP还有自己的输出错误消息的机制:trigger_error。它纯粹是一个帮助调试的工具,它不会取代标准的返回值。它非常适合您希望显示错误消息但仅仅是为了帮助开发人员。

如果一个函数足够复杂,可能导致需要以不同方式处理的几种不同类型的错误,那么您应该使用异常来执行此操作。

例如,一个非常简单的功能,目的明确,只需要返回truefalse

function isUserLoggedIn() {
    return $this->user == 'logged in';
}

具有可能无法实现此目的的功能:

function convertToFoo($bar) {
    if (!is_int($bar)) {
        return false;
    }
    // here be dragons
    return $foo;
}

同样可以触发消息的函数,对调试很有用:

function convertToFoo($bar) {
    if (!is_int($bar)) {
        trigger_error('$bar must be an int', E_USER_WARNING);
        return false;
    }
    // here be dragons
    return $foo;
}

可能合法地遇到调用代码需要知道的几种不同类型错误的函数:

function httpRequest($url) {
    ...

    if (/* could not connect */) {
        throw new CouldNotConnectException('Response code: ' . $code);
    }

    ...

    if (/* 404 */) {
        throw new PageNotFoundException('Page not found for ' . $url);
    }

    return true;
}

我也会在此粘贴此评论:

  

准备,返回功能不应该是功能的责任   或显示最终用户错误消息。如果功能的目的   比如,从数据库中获取内容,然后显示错误   消息不是它的业务。调用的代码   从数据库获取功能仅需要被告知   结果;从这里需要有代码,它的唯一工作就是   在数据库函数无法获取的情况下显示错误消息   需要的信息。不要混淆这两项责任。

答案 1 :(得分:4)

在特定情况下,返回具有多个元素但不是同类实体集合的数组的解决方案可能是可接受的解决方案。但总的来说这是一种代码味道。

这样的设计可能会暗示一些问题:

  • 您没有正确使用异常处理
  • 您的设计违反单一责任原则
  • 您总是等待返回直到方法结束

如果某个方法确实无法完成它应该做的事情,它应该不返回任何内容,它应该抛出异常。

如果您的方法只执行一项任务,则必须重构。

尽早返回。写方法更像这样:

if (!$something)
{
    return FALSE;
}

//do some other stuff
return 'great, it worked';

您的特定登录功能不应返回消息。这些特定于操作的用户消息应该由消息队列解耦和处理。

所以你可以将一个Messenger类注入你的控制器,这样你就可以在任何地方使用它并将消息添加到队列中。

function login($username, $password) 
{
    // Login logic here ..
    if ($logged_in) 
    {
        $this->messenger->addMessage('success', 'You are loggend in.');
        return TRUE;
    }
    {
        $this->messenger->addMessage('error', $message);
        return FALSE;
    }
}

答案 2 :(得分:3)

来自其他语言( off hand,C#)的常用习语是Do / TryDo方法配对。

/**
 * @param  MyInput $input
 * @return MyOutput
 * @throws MyException
 */
function myOperation(MyInput $input) 
{

}

myOperation 必须抛出异常(在此情况下为 MyException ),表示操作失败。成功时,将返回结果值(在这种情况下为 MyOutput )。

/**
 * @param  MyInput  $input
 * @param  MyOutput $output
 * @return bool
 */
function tryMyOperation(MyInput $input, &$output = null) 
{

}

tryMyOperation 必须返回一个布尔值,表示操作失败成功。它不得抛出异常(与操作的成功或失败直接相关)。该值将分配给引用传递的参数(在本例中为 $output )。通常,try*方法可以代理非try*方法。

一个人为的例子:

/**
 * @param  string $input
 * @return string
 * @throws TypeException
 */
function stringToUpper($input) 
{
    if (!is_string($input)) 
    {
        throw new TypeException('String expected');
    }
    return strtoupper($input);
}

/**
 * @param  string $input
 * @param  string $output
 * @return boolean
 */
function tryStringToUpper($input, &$output) 
{
    try 
    {
        $output = stringToUpper($input);
        return true;
    } 
    catch (TypeException $exception) 
    {
        $output = null;
        return false;
    }
}

并将用作:

try 
{
    $output = stringToUpper($input);
    // use $output
} 
catch (TypeException $exception) 
{
    // recover
}

if (tryStringToUpper($input, &$output)) 
{
    // use $output
} 
else 
{
    // recover
}

通过遵循这样的约定,代码的语义和意图变得更加清晰。如果要捕获错误信息,请使用myOperation()catch错误信息。如果你不关心为什么有什么东西坏了,而不是如果有什么东西坏了,请使用tryMyOperation()

答案 3 :(得分:1)

错误消息与返回值无关。 Antipatterns 可能有助于避免已知的编码错误。函数应返回与其目的一致的值,即:

canWriteFile() { return true or false }
writeFile() { should return void }

writeFile()根据程序员不期望任何价值的名称,他必须学习文档,这花费时间,不直观,可能导致错误。发明这样的名称,不需要任何文件。

你绝对不应该使用带有第一项bool,第二项错误消息的数组 - 这将返回复杂数据类型而不是简单直观的值,您将以编写适用于常见功能的适配器结束,并且您的代码很快就会变坏。

如何处理错误有三种可能性:

1)错误标志/状态对通知有用

$error = "";
function foo() {
  if($somethingBad) $error = "error occured";
  return !$somethingBad;
}

2)错误处理程序对大多数错误有用

function handleError($message) {
  ...
}

function foo() {
  if($somethingBad) handleError("error occured");
  return !$somethingBad;
}

3)捕获错误对无法处理的错误有用(即服务器在请求期间脱机)

function foo() {
  try {
    // dangerous code here
  }
  catch($e) {
    // handle error here
  }
  return !$somethingBad
}

答案 4 :(得分:1)

尝试例外

我建议稍微考虑一下Exceptions,因为它们可以派上用场。首先,你摆脱了返回的错误消息:你抛出异常。

其次,您不再需要true或false返回代码:如果每个事件都按预期工作,您就会知道这一点,因为不会抛出任何异常。

如果您在类中处理成功和错误消息,您可能还想摆脱它们。这样的事情应该在一个非常前端的类中处理,该类检查异常,然后根据异常或成功消息设置错误消息。

可重复使用的课程

方法应该返回应用程序可以使用的对象。当您开始将返回值用作通过系统传递的消息时,您依赖于此消息,这使得更换底层类很难。

一种好的思维方式如下:我可以在另一个项目中使用我的课吗?如果您有自定义错误消息,则可能很难,因为您可能需要其他项目中的其他人,并且必须更改您的课程。因此,您只想处理全局成功或错误(异常),然后在前端附近添加自定义错误消息。

答案 5 :(得分:-1)

标准方式显然是这样的:返回结果或错误。
出错时抛出异常。

答案 6 :(得分:-2)

这取决于您的应用正在做什么。在一个案例中,我不得不设计一个基于REST API的大型应用程序,我希望错误能够起泡并且对API的任何部分都是冗长的,直到用户级别。所以我做的是:

  1. 每个函数都返回一个错误/成功代码,这应该是有意义的。
  2. OK为1,“General Failure”为0,因此符合布尔值。
  3. 专门的错误代码是小于0的值。
  4. 您有一个枚举所有值的类,因此您不必记住代码。
  5. 此类还具有显示和记录错误的文本表示。
  6. 此类还有2个方法OK($errorCode)FAIL($errorCode),可在if内使用
  7. 这个类看起来像

    class Error {
    
        const OK = 1;
        //general error
        const FAIL = 0;
        //invalid url
        const RequestParseError = -1;
        //resource not found
        const ResourceNotFound = -2;
    
        ....
    
        static public function _($errCode) {
    
            switch ($errCode) {
    
                case Error::OK:
                    return "OK";
    
                case Error::FAIL:
                    return "General Failure";
    
                case Error::RequestParseError:
                    return "Request Parse Error";
    
            .....
        }
    
    }
    
    这样,应用程序的每个级别都可以返回错误代码,并且它会根据您的需要向上冒泡。并且OK()和FAIL()函数与布尔返回函数兼容。