函数调用中的多个参数与单个数组


24

我有一个函数,它接受一组参数,然后将它们作为条件应用于SQL查询。但是,尽管我赞成包含条件本身的单个参数数组:

function searchQuery($params = array()) {
    foreach($params as $param => $value) {
        switch ($param) {
            case 'name':
                $query->where('name', $value);
                break;
            case 'phone':
                $query->join('phone');
                $query->where('phone', $value);
                break;
        }
    }
}

我的同事更喜欢显式列出所有参数:

function searchQuery($name = '', $phone = '') {
    if ($name) {
        $query->where('name', $value);
    }

    if ($phone) {
        $query->join('phone');
        $query->where('phone', $value);
    }
}

他的论点是,通过显式列出参数,该函数的行为将变得更加明显-而不是必须深入代码以找出神秘的论点$param

我的问题是,在处理很多参数(例如10+)时,这变得非常冗长。有什么首选的做法吗?我最坏的情况是看到以下内容:

searchQuery('', '', '', '', '', '', '', '', '', '', '', '', 'search_query')


1
如果函数希望将特定键用作参数,则至少应将这些键记录在DocBlock中-这样,IDE可以显示相关信息,而不必深入研究代码。en.wikipedia.org/wiki/PHPDoc
Ilari Kajaste 2013年

2
性能提示:foreach在这种情况下,不需要,您可以使用if(!empty($params['name']))代替foreachswitch
13年

1
现在,您可以使用一种方法。我建议在这里看看:book.cakephp.org/2.0/en/models/…用于创建更多方法。它们甚至可以神奇地生成以用于标准查找,并可以自定义开发以用于特定搜索。通常,这为模型的用户提供了清晰的api。
Luc Franken


2
上面的“性能提示”上的注释:不要盲目地使用它!empty($params['name'])来测试参数-例如,字符串“ 0”为空。最好使用它array_key_exists来检查密钥,或者isset如果您不在乎null
AmadeusDrZaius

Answers:


27

恕我直言,您的同事对于上述示例是正确的。您的偏好可能很简洁,但其可读性也较差,因此难以维护。问一个问题,为什么要一开始就编写函数,您的函数“带到表”是什么—我必须详细地了解它的作用以及如何执行它,而仅仅是使用它。通过他的示例,即使我不是PHP程序员,我也可以在函数声明中看到足够多的细节,而不必担心其实现。

就大量参数而言,通常将其视为代码气味。通常,该函数尝试执行的操作太多吗?如果确实发现确实需要大量参数,则它们可能以某种方式关联并且一起属于一个或几个结构或类(甚至可能是相关项的数组,例如地址中的行)。但是,传递非结构化数组并不能解决代码异味。


对于需要大量参数的情况,该函数实质上是采用零个或多个参数,然后限制这些参数的结果集。参数本身彼此之间没有多大关系(作为不同的SQL子句),甚至可能没有相同的结构(一个参数可以是简单的WHERE,但另一个参数除了WHERE之外还需要多个JOIN)。在这种特定情况下,它还会被视为代码气味吗?
xiankai 2013年

2
@xiankai在该示例中,我可能会为where参数创建一个数组参数,为join说明符创建一个数组参数。通过适当地命名它们,这些参数仍然可以自我记录。
2013年

如果我改用setter / getter而根本不传递参数怎么办?这是不好的做法吗?这不是使用setter / getter的目的吗?
lyhong 2015年

我会质疑OP的偏好是“可读性较差”(如何?)且难以维护。与searchQuery([ 'x'=>'bar'])大量参数也不一定是代码气味。例如query()。即使对于较少数量的参数,直接传递参数时发生的按参数顺序缺乏一致性也说明了对参数进行硬编码是一个坏主意。只需查看PHP中的字符串和数组函数即可得出不一致的信息。
MikeSchinkel '18

4

我的回答或多或少与语言无关。

如果在复杂数据结构(表,记录,字典,对象...)中对参数进行分组的唯一目的是将它们作为一个整体传递给一个函数,则最好避免使用它。这增加了一层毫无用处的复杂性,并使您的意图变得晦涩。

如果分组的参数本身具有含义,则该复杂性层有助于理解整个设计:将其命名为抽象层。

您可能会发现,最好的设计是使用两个或三个参数,而不是将多个参数或一个大型数组组合在一起,以对相关数据进行分组。


1

就您而言,我更喜欢您同事的方法。如果您正在编写模型,而我正在使用您的模型来开发它们。我看到了您同事方法的签名,可以立即使用。

同时,我将不得不执行您的searchQuery函数,以查看您的函数需要哪些参数。

我只在searchQuery只限于在单个表中搜索的情况下才使用您的方法,因此不会有任何联接。在这种情况下,我的函数将如下所示:

function searchQuery($params = array()) {
    foreach($params as $param => $value) {
        $query->where($param, $value);
    }
} 

因此,我立即知道array的元素实际上是具有该方法的类在您的代码中表示的特定的列名。


1

两者都做。 array_merge就像您的同事喜欢的那样,允许在函数顶部显示一个显式列表,同时根据您的喜好避免参数变得笨拙。

我也强烈建议在问题注释中使用@chiborg的建议-您的意图要清楚得多。

function searchQuery($params = array()) {
    $defaults = array(
        'name' => '',
        'phone' => '',
        ....
    );
    $params = array_merge($defaults, $params);

    if(!empty($params['name'])) {
        $query->where('name', $params['name']);
    }
    if (!empty($params['phone'])) {
        $query->join('phone');
        $query->where('phone', $params['phone']);
    }
    ....
}

0

另外,您可以传递类似于查询字符串的字符串,并使用parse_str(因为您似乎在使用PHP,但是其他解决方案可能在其他语言中可用)将其处理为方法内部的数组:

/**
 * Executes a search in the DB with the constraints specified in the $queryString
 * @var $queryString string The search parameters in a query string format (ie
 *      "foo=abc&bar=hello"
 * @return ResultSet the result set of performing the query
 */
function searchQuery($queryString) {
  $params = parse_str($queryString);
  if (isset($params['name'])) {
    $query->where('name', $params['name']);
  }
  if (isset($params['phone'])) {
    $query->join('phone');
    $query->where('phone', $params['phone']);
  }
  ...

  return ...;
}

并称它为

$result = searchQuery('name=foo&phone=555-123-456');

您可以http_build_query用来将关联数组转换为字符串(相反parse_str)。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.