Кілька аргументів у виклику функції проти одного масиву


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
Іларі Каясте

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, я можу побачити достатньо деталей у декларації функції, що мені не доводиться турбуватися про її виконання.

Що стосується більшої кількості аргументів, це зазвичай вважається запахом коду. Зазвичай функція намагається зробити занадто багато? Якщо ви дійсно потребуєте великої кількості аргументів, ймовірно, вони пов'язані якимось чином і належать разом до однієї або кількох структур або класів (можливо, навіть масив пов’язаних елементів, таких як рядки в адресі). Однак передача неструктурованого масиву нічого не робить для вирішення коду запахів.


Що стосується необхідності великої кількості аргументів, функція по суті бере нульовий або більше аргументів, а потім обмежує результат, встановлений цими аргументами. Самі аргументи не мають великого відношення один до одного (як окремі пункти SQL) і можуть навіть не мати однакової структури (один може бути простим, де БЕЗ, але інший потребує декількох ПРИЄДНАННЯ на додаток до WHERE). Чи все-таки вважатиметься кодовим запахом у цьому конкретному випадку?
xiankai

2
@xiankai У цьому прикладі я міг би зробити один параметр масиву для whereаргументів, один для joinспецифікаторів тощо, назвавши їх відповідним чином, що все-таки було б самодокументуванням .
Ян Догген

Що робити, якщо замість цього я використовую setter / getter і взагалі не передаю аргументи? Це погана практика? Чи це не мета використання сеттера / геттера?
Ліхонг

Я б заперечив, що уподобання ОП є "менш читабельним" (як?) Та менш реальним. searchQuery ('', '', '', '', 'foo', '', '', '', 'bar') набагато менш читабельний або піддається технічному обслуговуванню, ніж searchQuery (['q' => 'foo', 'x' => 'bar']) Велика кількість аргументів необов'язково є і кодовим запахом; запит (), наприклад. І навіть для меншої кількості аргументів, відсутність послідовності порядку аргументів, що виникає при передачі аргументів, прямо ілюструє, яка погана ідея щодо параметрів жорсткого коду. Просто подивіться на функції рядків і масивів у PHP для виявлення невідповідності.
MikeSchinkel

4

Моя відповідь - більш-менш агностична мова.

Якщо єдиною метою групування аргументів у складній структурі даних (таблиця, запис, словник, об’єкт ...) є передача їх у цілому функції, краще уникати цього. Це додає марного шару складності та робить ваш намір незрозумілим.

Якщо згруповані аргументи мають значення самі по собі, то цей рівень складності допомагає зрозуміти всю конструкцію: замість цього назвіть її шар абстракції.

Ви можете виявити, що замість дюжини окремих аргументів або одного великого масиву найкращий дизайн - це два або три аргументи, які групують корельовані дані.


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']);
    }
    ....
}

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це робить).

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.