Argomenti multipli nella chiamata di funzione vs singolo array


24

Ho una funzione che accetta una serie di parametri, quindi li applica come condizioni a una query SQL. Tuttavia, mentre ho preferito un singolo array di argomenti contenente le condizioni stesse:

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

Il mio collega ha preferito elencare esplicitamente tutti gli argomenti invece:

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

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

La sua argomentazione era che elencando gli argomenti esplicitamente, il comportamento della funzione diventa più evidente - al contrario di dover approfondire il codice per scoprire quale fosse l'argomento misterioso $param.

Il mio problema era che questo diventa molto dettagliato quando si affrontano molti argomenti, come 10+. C'è qualche pratica preferita? Il mio scenario peggiore sarebbe vedere qualcosa di simile al seguente:

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


1
Se la funzione prevede chiavi specifiche come parametri, almeno quelle chiavi dovrebbero essere documentate in un DocBlock, in questo modo gli IDE possono mostrare le informazioni rilevanti senza dover approfondire il codice. en.wikipedia.org/wiki/PHPDoc
Ilari Kajaste

2
Suggerimento per le prestazioni: foreachnon è necessario in questo caso, è possibile utilizzare al if(!empty($params['name']))posto di foreache switch.
chiborg,

1
Ora hai un metodo che usi. Vorrei suggerire di dare un'occhiata qui: book.cakephp.org/2.0/en/models/… per creare più metodi. Possono anche essere generati magicamente per reperti standard e sviluppati in modo personalizzato per ricerche specifiche. In generale, ciò rende un'api chiara per gli utenti del modello.
Luc Franken,


2
Una nota sul 'suggerimento prestazionale' sopra: non usare ciecamente !empty($params['name'])per testare i parametri - per esempio, la stringa "0" sarebbe vuota. È meglio usare array_key_existsper verificare la chiave, o issetse non ti interessa null.
Amadeus DrZaius,

Risposte:


27

IMHO il tuo collega è corretto per l'esempio sopra. La tua preferenza potrebbe essere concisa, ma è anche meno leggibile e quindi meno gestibile. Poni la domanda perché preoccuparsi di scrivere la funzione in primo luogo, che cosa porta la tua funzione al tavolo? Devo capire cosa fa e come lo fa, in grande dettaglio, solo per usarlo. Con il suo esempio, anche se non sono un programmatore di PHP, nella dichiarazione della funzione posso vedere abbastanza dettagli da non dovermi preoccupare della sua implementazione.

Per quanto riguarda un numero maggiore di argomenti, questo è normalmente considerato un odore di codice. In genere la funzione sta cercando di fare troppo? Se trovi un reale bisogno di un gran numero di argomenti, è probabile che siano collegati in qualche modo e appartengano insieme in una o poche strutture o classi (forse anche array di elementi correlati come le righe in un indirizzo). Tuttavia, il passaggio di un array non strutturato non fa nulla per indirizzare gli odori del codice.


Per quanto riguarda la necessità di un gran numero di argomenti, la funzione sta essenzialmente prendendo zero o più argomenti e quindi limita il risultato impostato da tali argomenti. Gli argomenti stessi non hanno molto a che fare l'uno con l'altro (come distinte clausole SQL) e potrebbero anche non avere la stessa struttura (uno può essere un semplice DOVE, ma un altro richiederebbe diversi JOIN oltre a DOVE). In questo caso specifico sarebbe ancora considerato un odore di codice?
Xiankai,

2
@xiankai In questo esempio, potrei forse creare un parametro array per whereargomenti, uno per gli joinspecificatori, ecc. Nominandoli in modo appropriato sarebbe comunque auto-documentante.
Jan Doggen,

Cosa succede se uso setter / getter e non passo affatto argomento? È una cattiva pratica? Non è uno scopo di usare setter / getter?
Lyhong,

Sfiderei che la preferenza del PO sia "meno leggibile" (come?) E meno mantenibile. searchQuery ('', '', '', '', 'foo', '', '', '', 'bar') è molto meno leggibile o gestibile di searchQuery (['q' => 'foo', 'x' => 'bar']) Un gran numero di argomenti non è necessariamente un odore di codice; una query (), ad esempio. E anche per un numero inferiore di argomenti, la mancanza di coerenza nell'ordine degli argomenti che si verifica quando gli argomenti vengono passati illustra direttamente quale cattiva idea è quella di codificare i parametri. Basta guardare le funzioni di stringa e matrice in PHP per detta incoerenza.
MikeSchinkel,

4

La mia risposta è più o meno agnostica della lingua.

Se l'unico scopo del raggruppamento di argomenti in una struttura di dati complessa (tabella, record, dizionario, oggetto ...) è di passarli nel loro insieme a una funzione, meglio evitarlo. Ciò aggiunge un inutile livello di complessità e rende oscura la tua intenzione.

Se gli argomenti raggruppati hanno un significato da soli, allora quel livello di complessità aiuta a comprendere l'intero progetto: chiamalo invece livello di astrazione.

Potresti scoprire che invece di una dozzina di singoli argomenti o un grande array, il miglior design è con due o tre argomenti ciascuno che raggruppa i dati correlati.


1

Nel tuo caso, preferirei il metodo del tuo collega. Se stavi scrivendo modelli e stavo usando i tuoi modelli per svilupparli. Vedo la firma del metodo del tuo collega e posso usarlo immediatamente.

Mentre, dovrei passare attraverso l'implementazione della tua searchQueryfunzione per vedere quali parametri sono previsti dalla tua funzione.

Preferirei il tuo approccio solo nel caso in cui searchQuerysia limitato alla ricerca all'interno di un'unica tabella, quindi non ci saranno join. In tal caso la mia funzione sarebbe simile a questa:

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

Quindi, so immediatamente che gli elementi dell'array sono in realtà i nomi di colonna di una particolare tabella che la classe che ha questo metodo rappresenta nel tuo codice.


1

Fai entrambe le cose. array_mergeconsente un elenco esplicito nella parte superiore della funzione, come piace al tuo collega, evitando che i parametri diventino ingombranti, come preferisci.

Consiglio anche vivamente di utilizzare il suggerimento di @ chiborg dai commenti alle domande - è molto più chiaro ciò che intendi.

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

Inoltre potresti passare una stringa simile a una stringa di query e utilizzare parse_str(perché sembra che tu stia utilizzando PHP, ma altre soluzioni sono probabilmente disponibili in altre lingue) per elaborarlo in un array all'interno del metodo:

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

e chiamalo come

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

È possibile utilizzare http_build_queryper convertire da un array associativo a una stringa (il contrario che parse_strfa).

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.