Несколько аргументов в вызове функции против одного массива

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')

xiankai
источник
1
Если функция ожидает определенные ключи в качестве параметров, по крайней мере, эти ключи должны быть задокументированы в DocBlock - таким образом, IDE могут отображать соответствующую информацию без необходимости углубляться в код. en.wikipedia.org/wiki/PHPDoc
Илари Каджасте,
2
Совет по производительности: foreachв этом случае ненужно, вы можете использовать if(!empty($params['name']))вместо foreachи switch.
Чиборг
1
Теперь у вас есть один метод, который вы используете. Я бы посоветовал взглянуть здесь: book.cakephp.org/2.0/en/models/… для создания дополнительных методов. Они могут даже волшебным образом генерироваться для стандартных находок и разрабатываться на заказ для конкретных поисков. В целом, это делает ясным API для пользователей модели.
Люк Франкен
2
Примечание к «совету по производительности» выше: не используйте вслепую !empty($params['name'])для проверки параметров - например, строка «0» будет пустой. Лучше использовать, array_key_existsчтобы проверить ключ, или, issetесли вам все равно null.
AmadeusDrZaius

Ответы:

27

ИМХО твой коллега подходит для приведенного выше примера. Ваше предпочтение может быть кратким, но также менее читабельным и, следовательно, менее понятным. Задайте вопрос, зачем вообще писать эту функцию, что ваша функция «выводит на стол» - я должен понять, что она делает и как она делает, очень подробно, просто чтобы ее использовать. На его примере, хотя я не программист PHP, я могу видеть достаточно подробностей в объявлении функции, поэтому мне не нужно беспокоиться о ее реализации.

Что касается большего количества аргументов, это обычно считается запахом кода. Как правило, функция пытается сделать слишком много? Если вы обнаружите реальную потребность в большом количестве аргументов, скорее всего, они каким-то образом связаны и принадлежат одной или нескольким структурам или классам (возможно, даже массиву связанных элементов, таких как строки в адресе). Однако передача неструктурированного массива ничего не делает для устранения запахов кода.

mattnz
источник
Что касается необходимости большого количества аргументов, функция по существу принимает ноль или более аргументов, а затем ограничивает набор результатов этими аргументами. Сами аргументы не имеют большого отношения друг к другу (как отдельные предложения SQL) и могут даже не иметь одинаковую структуру (один может быть простым WHERE, а другому потребуется несколько JOIN в дополнение к WHERE). Будет ли это все еще считаться запахом кода в этом конкретном случае?
Сянкай
2
@xiankai В этом примере я мог бы создать один параметр массива для whereаргументов, один для joinспецификаторов и т. д. При правильном присвоении им имен это все равно будет самодокументироваться.
Ян Догген
Что, если я вместо этого использую setter / getter и не передаю аргументы вообще? Это плохая практика? Разве это не цель использования сеттера / геттера?
Lyhong
Я бы поспорил, что предпочтение ФП «менее читабельно» (как?) И менее поддерживаемо. searchQuery ('', '', '', '', 'foo', '', '', '', 'bar') гораздо менее читабелен или удобен в обслуживании, чем searchQuery (['q' => 'foo', 'x' => 'bar']) Большое количество аргументов также не обязательно является запахом кода; запрос (), например. И даже для меньшего числа аргументов, отсутствие согласованности в порядке аргументов, которое происходит, когда аргументы передаются напрямую, показывает, какая плохая идея жестко кодировать параметры. Просто посмотрите на строковые и массивные функции в PHP на предмет несоответствия.
MikeSchinkel
4

Мой ответ более или менее зависит от языка.

Если единственной целью группировки аргументов в сложной структуре данных (таблица, запись, словарь, объект ...) является передача их целиком в функцию, лучше избегать этого. Это добавляет бесполезный слой сложности и делает ваше намерение неясным.

Если сгруппированные аргументы сами по себе имеют значение, то этот уровень сложности помогает понять весь дизайн: вместо этого назовите его слоем абстракции.

Возможно, вы обнаружите, что вместо дюжины отдельных аргументов или одного большого массива лучше всего использовать два или три аргумента, каждый из которых группирует коррелированные данные.

mouviciel
источник
1

В вашем случае я бы предпочел метод вашего коллеги. Если вы писали модели, а я использовал ваши модели, чтобы развиваться над ними. Я вижу подпись метода вашего коллеги и могу использовать его немедленно.

Пока я должен был бы пройти через реализацию вашей searchQueryфункции, чтобы увидеть, какие параметры ожидаются вашей функцией.

Я бы предпочел ваш подход только в том случае, если searchQueryпоиск ограничен только одной таблицей, поэтому объединений не будет. В этом случае моя функция будет выглядеть так:

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

Итак, я сразу знаю, что элементы массива на самом деле являются именами столбцов конкретной таблицы, которую класс, имеющий этот метод, представляет в вашем коде.

Озаир Кафрай
источник
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']);
    }
    ....
}
Izkata
источник
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).

Карлос Кампдеррос
источник