Mehrere Argumente im Funktionsaufruf gegen ein einzelnes Array

24

Ich habe eine Funktion, die eine Reihe von Parametern aufnimmt und diese dann als Bedingungen für eine SQL-Abfrage anwendet. Ich bevorzuge jedoch ein einzelnes Argument-Array, das die Bedingungen selbst enthält:

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

Mein Kollege hat es vorgezogen, alle Argumente explizit aufzulisten:

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

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

Sein Argument war, dass durch die explizite Auflistung der Argumente das Verhalten der Funktion offensichtlicher wird - anstatt sich mit dem Code befassen zu müssen, um herauszufinden, was das mysteriöse Argument $paramwar.

Mein Problem war, dass dies sehr ausführlich wird, wenn man mit vielen Argumenten wie 10+ umgeht. Gibt es eine bevorzugte Praxis? In meinem Worst-Case-Szenario würde etwa Folgendes auftreten:

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

xiankai
quelle
1
Wenn die Funktion bestimmte Schlüssel als Parameter erwartet, sollten zumindest diese Schlüssel in einem DocBlock dokumentiert werden. Auf diese Weise können IDEs die relevanten Informationen anzeigen, ohne sich mit dem Code befassen zu müssen. en.wikipedia.org/wiki/PHPDoc
Ilari Kajaste
2
Leistungstipp: Das foreachist in diesem Fall unnötig, Sie könnten if(!empty($params['name']))statt foreachund verwenden switch.
Chiborg
1
Sie haben jetzt eine Methode, die Sie verwenden. Ich würde vorschlagen, hier einen Blick darauf zu werfen: book.cakephp.org/2.0/en/models/…, um weitere Methoden zu erstellen. Sie können sogar auf magische Weise für Standardfunde generiert und für bestimmte Suchvorgänge individuell entwickelt werden. Im Allgemeinen macht das eine klare API für die Benutzer des Modells.
Luc Franken
2
Ein Hinweis zum 'Leistungstipp' oben: Nicht blind verwenden !empty($params['name']), um auf Parameter zu testen - zum Beispiel wäre die Zeichenfolge "0" leer. Es ist besser, array_key_existsnach dem Schlüssel zu suchen, oder issetwenn Sie sich nicht darum kümmern null.
AmadeusDrZaius

Antworten:

27

IMHO ist Ihr Kollege für das obige Beispiel richtig. Ihre Präferenz mag knapp sein, aber es ist auch weniger lesbar und daher weniger wartbar. Stellen Sie die Frage, warum Sie sich überhaupt die Mühe machen, die Funktion zu schreiben, was Ihre Funktion "auf den Tisch bringt" - ich muss verstehen, was sie tut und wie sie es ausführlich tut, nur um sie zu verwenden. Mit seinem Beispiel kann ich, obwohl ich kein PHP-Programmierer bin, genügend Details in der Funktionsdeklaration erkennen, so dass ich mich nicht um deren Implementierung kümmern muss.

Bei einer größeren Anzahl von Argumenten wird dies normalerweise als Codegeruch betrachtet. Normalerweise versucht die Funktion zu viel zu tun? Wenn Sie tatsächlich eine große Anzahl von Argumenten benötigen, ist es wahrscheinlich, dass sie in irgendeiner Weise zusammenhängen und in einer oder wenigen Strukturen oder Klassen zusammengehören (möglicherweise sogar in einer Reihe zusammenhängender Elemente, wie z. B. Zeilen in einer Adresse). Das Übergeben eines unstrukturierten Arrays hat jedoch keine Auswirkung auf die Codegerüche.

mattnz
quelle
Da eine große Anzahl von Argumenten benötigt wird, akzeptiert die Funktion im Wesentlichen null oder mehr Argumente und schränkt dann die Ergebnismenge durch diese Argumente ein. Die Argumente selbst haben nicht viel miteinander zu tun (als unterschiedliche SQL-Klauseln) und haben möglicherweise nicht die gleiche Struktur (eines kann ein einfaches WHERE sein, ein anderes erfordert jedoch zusätzlich zum WHERE mehrere JOINs). Wäre es in diesem speziellen Fall immer noch ein Codegeruch?
Xiankai
2
@xiankai In diesem Beispiel würde ich vielleicht einen Array-Parameter für whereArgumente, einen für joinBezeichner usw. festlegen .
Jan Doggen
Was ist, wenn ich stattdessen setter / getter verwende und überhaupt kein Argument übergebe? Ist es eine schlechte Praxis? Ist es nicht ein Zweck von Setter / Getter?
Lyhong
Ich würde herausfordern, dass die Präferenz des OP "weniger lesbar" (wie?) Und weniger wartbar ist. searchQuery ('', '', '', '', 'foo', '', '', '', 'bar') ist weitaus weniger lesbar oder wartbar als searchQuery (['q' => 'foo', 'x' => 'bar']) Eine große Anzahl von Argumenten ist nicht unbedingt ein Code-Geruch. Zum Beispiel eine query (). Und selbst bei einer geringeren Anzahl von Argumenten zeigt der Mangel an Konsistenz in der Argumentreihenfolge, der auftritt, wenn Argumente direkt übergeben werden, was für eine schlechte Idee es ist, Parameter fest zu codieren. Schauen Sie sich einfach die String- und Array-Funktionen in PHP an, um diese Inkonsistenz zu ermitteln.
MikeSchinkel
4

Meine Antwort ist mehr oder weniger sprachunabhängig.

Wenn der einzige Zweck der Gruppierung von Argumenten in einer komplexen Datenstruktur (Tabelle, Datensatz, Wörterbuch, Objekt ...) darin besteht, sie als Ganzes an eine Funktion zu übergeben, sollten Sie dies besser vermeiden. Dies fügt eine nutzlose Komplexitätsebene hinzu und macht Ihre Absicht undeutlich.

Wenn die gruppierten Argumente eine eigene Bedeutung haben, hilft diese Komplexitätsebene beim Verständnis des gesamten Designs: Nennen Sie sie stattdessen Abstraktionsebene.

Möglicherweise stellen Sie fest, dass anstelle von einem Dutzend einzelner Argumente oder eines großen Arrays das beste Design zwei oder drei Argumente sind, die jeweils korrelierte Daten gruppieren.

mouviciel
quelle
1

In Ihrem Fall würde ich die Methode Ihres Kollegen vorziehen. Wenn Sie Modelle schreiben und ich Ihre Modelle verwende, um sie zu überarbeiten. Ich sehe die Unterschrift der Methode Ihres Kollegen und kann sie sofort verwenden.

Währenddessen müsste ich die Implementierung Ihrer searchQueryFunktion durchgehen, um zu sehen, welche Parameter von Ihrer Funktion erwartet werden.

Ich würde Ihren Ansatz nur in dem Fall bevorzugen, in dem searchQuerynur innerhalb einer einzelnen Tabelle gesucht werden soll, sodass es keine Verknüpfungen gibt. In diesem Fall würde meine Funktion so aussehen:

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

Ich weiß also sofort, dass die Elemente von array tatsächlich die Spaltennamen einer bestimmten Tabelle sind, die die Klasse mit dieser Methode in Ihrem Code darstellt.

Ozair Kafray
quelle
1

Tun Sie beides. array_mergeErmöglicht eine explizite Liste am oberen Rand der Funktion, die Ihrem Kollegen gefällt, und verhindert, dass die Parameter nach Belieben unhandlich werden.

Ich empfehle auch dringend, @ chiborgs Vorschlag aus den Fragenkommentaren zu verwenden - es ist viel klarer, was Sie beabsichtigen.

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
quelle
0

Sie können auch eine Zeichenfolge übergeben, die einer Abfragezeichenfolge ähnelt, und verwenden parse_str(da Sie anscheinend PHP verwenden, andere Lösungen jedoch wahrscheinlich in anderen Sprachen verfügbar sind), um sie in ein Array innerhalb der Methode zu verarbeiten:

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

und nenne es wie

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

Mit können Sie http_build_queryein assoziatives Array in eine Zeichenfolge konvertieren (umgekehrt parse_str).

Carlos Campderrós
quelle