Wiele argumentów w wywołaniu funkcji vs pojedyncza tablica

24

Mam funkcję, która przyjmuje zestaw parametrów, a następnie stosuje się do nich jako warunki do zapytania SQL. Jednakże, chociaż faworyzowałem tablicę pojedynczych argumentów zawierającą same warunki:

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;
        }
    }
}

Mój kolega wolał zamiast tego jawnie wymienić wszystkie argumenty:

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

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

Jego argumentem było to, że poprzez jawne wyszczególnienie argumentów, zachowanie funkcji staje się bardziej widoczne - w przeciwieństwie do konieczności zagłębiania się w kod, aby dowiedzieć się, co to $parambył za tajemniczy argument .

Mój problem polegał na tym, że staje się to bardzo gadatliwe, gdy mamy do czynienia z wieloma argumentami, takimi jak 10+. Czy jest jakaś preferowana praktyka? Mój najgorszy scenariusz wyglądałby następująco:

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

Xiankai
źródło
1
Jeśli funkcja oczekuje określonych kluczy jako parametrów, przynajmniej te klucze powinny być udokumentowane w DocBlock - w ten sposób IDE mogą wyświetlać odpowiednie informacje bez konieczności zagłębiania się w kod. en.wikipedia.org/wiki/PHPDoc
Ilari Kajaste
2
Wskazówka dotycząca wydajności: foreachw tym przypadku nie jest konieczne, możesz użyć if(!empty($params['name']))zamiast foreachi switch.
chiborg
1
Masz teraz jedną metodę, której używasz. Proponuję zajrzeć tutaj: book.cakephp.org/2.0/en/models/…, aby uzyskać więcej metod. Mogą być nawet magicznie generowane dla standardowych znalezisk i opracowywane niestandardowo dla określonych wyszukiwań. Zasadniczo stanowi to wyraźny interfejs API dla użytkowników modelu.
Luc Franken
2
Uwaga na powyższą „wskazówkę dotyczącą wydajności”: nie używaj !empty($params['name'])na ślepo do testowania parametrów - na przykład ciąg „0” byłby pusty. Lepiej użyć, array_key_existsaby sprawdzić klucz, lub issetjeśli cię to nie obchodzi null.
AmadeusDrZaius

Odpowiedzi:

27

IMHO twój kolega ma rację dla powyższego przykładu. Twoje preferencje mogą być zwięzłe, ale są również mniej czytelne, a zatem mniej konserwowalne. Zadaj pytanie, po co zawracać sobie głowę pisaniem funkcji, co twoja funkcja „przynosi do stołu” - muszę zrozumieć, co robi i jak to robi, bardzo szczegółowo, po prostu z niej korzystać. Na jego przykładzie, mimo że nie jestem programistą PHP, widzę wystarczająco dużo szczegółów w deklaracji funkcji, że nie muszę się martwić o jej implementację.

Jeśli chodzi o większą liczbę argumentów, jest to zwykle uważane za zapach kodu. Zazwyczaj funkcja próbuje zrobić zbyt wiele? Jeśli naprawdę potrzebujesz dużej liczby argumentów, prawdopodobnie są one w jakiś sposób powiązane i należą do jednej lub kilku struktur lub klas (może nawet tablicy powiązanych elementów, takich jak linie w adresie). Jednak przekazanie nieustrukturyzowanej tablicy nie rozwiązuje problemu zapachów kodu.

mattnz
źródło
Jeśli chodzi o potrzebę dużej liczby argumentów, funkcja zasadniczo przyjmuje zero lub więcej argumentów, a następnie ogranicza wynik ustawiony przez te argumenty. Same argumenty nie mają wiele wspólnego ze sobą (jako odrębne klauzule SQL) i mogą nawet nie mieć tej samej struktury (jeden może być prosty GDZIE, ale inny wymagałby kilku JOIN oprócz GDZIE). Czy w tym konkretnym przypadku nadal byłby to zapach zapachowy?
xiankai
2
@xiankai W tym przykładzie mógłbym zrobić jedną tablicę param dla whereargumentów, drugą dla joinspecyfikatorów itp. Nadając im odpowiednie nazwy, nadal byłyby to dokumenty.
Jan Doggen
Co jeśli użyję zamiast tego setter / getter i nie przekażę argumentów? Czy to zła praktyka? Czy to nie jest cel używania setera / gettera?
lyhong,
Podważałbym, że preferencje PO są „mniej czytelne” (jak?) I mniej konserwowalne. searchQuery („”, „”, „”, „”, „foo”, „”, „”, „”, „bar”) jest znacznie mniej czytelny lub możliwy do utrzymania niż searchQuery (['q' => 'foo', „x” => „bar”]) Duża liczba argumentów niekoniecznie jest zapachem kodu; zapytanie (), na przykład. I nawet w przypadku mniejszej liczby argumentów brak spójności w kolejności argumentów, który występuje, gdy argumenty są przekazywane bezpośrednio, pokazuje, jaki zły jest pomysł na parametry kodu. Wystarczy spojrzeć na funkcje łańcuchowe i tablicowe w PHP na wspomnianą niespójność.
MikeSchinkel
4

Moja odpowiedź jest mniej lub bardziej zależna od języka.

Jeśli jedynym celem grupowania argumentów w złożonej strukturze danych (tabela, rekord, słownik, obiekt ...) jest przekazanie ich jako całości do funkcji, lepiej jej unikać. Dodaje to niepotrzebną warstwę złożoności i sprawia, że ​​twoja intencja staje się niejasna.

Jeśli pogrupowane argumenty mają znaczenie same w sobie, wówczas ta warstwa złożoności pomaga zrozumieć cały projekt: zamiast tego nazwij ją warstwą abstrakcji.

Może się okazać, że zamiast kilkunastu pojedynczych argumentów lub jednej dużej tablicy, najlepszym rozwiązaniem są dwa lub trzy argumenty, z których każdy grupuje skorelowane dane.

mouviciel
źródło
1

W twoim przypadku wolałbym metodę twojego kolegi. Jeśli pisałeś modele, a ja korzystałem z twoich modeli, aby się nad nimi rozwijać. Widzę podpis metody twojego kolegi i mogę go natychmiast użyć.

Będę musiał przejść przez implementację Twojej searchQueryfunkcji, aby zobaczyć, jakich parametrów oczekuje Twoja funkcja.

Wolę twoje podejście tylko w przypadku, gdy searchQueryogranicza się do wyszukiwania tylko w obrębie jednej tabeli, więc nie będzie żadnych połączeń. W takim przypadku moja funkcja wyglądałaby następująco:

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

Tak więc od razu wiem, że elementy tablicy są w rzeczywistości nazwami kolumn konkretnej tabeli, którą klasa posiadająca tę metodę reprezentuje w twoim kodzie.

Ozair Kafray
źródło
1

Rób oba, w pewnym sensie. array_mergepozwala na wyświetlenie wyraźnej listy na górze funkcji, jak lubi twój kolega, przy jednoczesnym utrzymaniu parametrów nieporęcznych, jak wolisz.

Zdecydowanie sugeruję również skorzystanie z sugestii @ chiborg z komentarzy do pytań - jest to o wiele jaśniejsze, co zamierzasz.

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
źródło
0

Możesz również przekazać ciąg podobny do ciągu zapytania i użyć parse_str(ponieważ wygląda na to, że używasz PHP, ale inne rozwiązania są prawdopodobnie dostępne w innych językach), aby przetworzyć go na tablicę wewnątrz metody:

/**
 * 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 ...;
}

i nazwać to tak

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

Możesz użyć http_build_querydo konwersji z tablicy asocjacyjnej na ciąg znaków (odwrotnie parse_str).

Carlos Campderrós
źródło