PHP: quando usare gli array e quando usare gli oggetti per costrutti di codice per lo più di archiviazione dei dati?


37

PHP è un linguaggio di paradigma misto, che consente di utilizzare e restituire tipi di dati non oggetto, come gli array. Pongo una domanda per cercare di chiarire alcune linee guida per la selezione di matrici contro oggetti quando decido quale costrutto di programmazione usare in una particolare situazione.

Questa è davvero una domanda sui modi per codificare i dati usando i costrutti del linguaggio PHP e quando è più probabile che un modo sia scelto su un altro per scopi di trasferimento dei dati (ovvero architettura orientata ai servizi o servizi web).

Esempio

Supponiamo di avere un tipo di articolo costituito da {costo, nome, numero_part, numero_articolo}. Il programma richiede la visualizzazione di diversi di questi tipi di elementi, in cui si decide di utilizzare un array come contenitore esterno per contenere ciascuno dei tipi di elementi. [Puoi anche usare i PHP ArrayObjectper il paradigma OO, ma la mia domanda non riguarda quell'array (esterno)]. La mia domanda è su come codificare i dati del tipo di elemento e su quale paradigma usare. PHP ti permette di usare PHP Native Arrayso PHP Objects.

Posso codificare tali dati, in due modi qui, in questo modo:

//PHP's associative arrays:
$ret = array(
    0 => array(
        'cost' => 10.00, 
        'name' => 'item1',
        'part_number' => 'zyz-100', 
        'item_count' => 15
        ),
    1 => array(
        'cost' => 34.00, 
        'name' => 'item2', 
        'part_number' => 'abc-230', 
        'item_count' => 42
        ),
  );

vs

//here ItemType is encapsulated into an object
$ret = array(
  0 => new ItemType(10.00, 'item1', 'zyz-100', 15),
  1 => new ItemType(34.00, 'item2', 'abc-230', 42),
);

class ItemType
{
    private $price;
    private $name;
    private $partNumber;
    private $itemCount;

    function __construct($price, $name, $partNumber, $itemCount) {..}
}

Quello che sto pensando

La codifica dell'array è leggera e più predisposta per JSON, ma può essere più facile da confondere. Digitare erroneamente una delle chiavi dell'array associativo e si potrebbe avere un errore che è più difficile da rilevare. Ma è anche più facile cambiare per capriccio. Supponiamo che non voglia item_countpiù archiviare , posso utilizzare qualsiasi software di elaborazione testi per rimuovere facilmente tutte le item_countistanze dell'array e quindi aggiornare altre funzioni che lo utilizzano di conseguenza. Potrebbe essere un processo più noioso, ma è semplice.

La codifica orientata agli oggetti fa appello alle strutture del linguaggio IDE e PHP e facilita la rilevazione preventiva di eventuali errori, ma è più difficile programmare e codificare in primo luogo. Dico di più, perché devi pensare un po 'ai tuoi oggetti, pensare in anticipo e la codifica OO richiede un carico cognitivo un po' più elevato rispetto alla digitazione di strutture di array. Detto questo, una volta codificato, alcune modifiche potrebbero essere più facili da implementare, in un certo senso, la rimozione item_count, ad esempio, richiederà la modifica di meno righe di codice. Ma i cambiamenti stessi possono ancora richiedere un carico cognitivo più elevato rispetto al metodo array, poiché sono coinvolte strutture OO di livello superiore.

Domanda

In alcuni casi è chiaro, come i casi in cui dovrò eseguire manipolazioni sui dati. Ma in alcuni casi, dove devo solo memorizzare alcune righe di dati "Tipo di elemento", non ho linee guida chiare o considerazioni su cui basarmi quando provo a decidere se usare gli array o se costruire oggetti. Sembra che posso semplicemente lanciare una moneta e sceglierne una. È questo il caso qui?


2
Nota è possibile ottenere un oggetto leggero lanciando una matrice a un oggetto: (object)['foo'=>'bar']. Il valore risultante ha classe StdClass, sarà codificato JSON nella sua interezza da json_encode()e le proprietà possono essere rinominate con la stessa facilità degli indici di array (che non è sempre così facile, quando si accede indirettamente tramite una variabile). Tuttavia, ci sono diverse operazioni su tali valori; ad esempio, non si hanno unioni di oggetti come si hanno unioni di array e non è possibile utilizzare direttamente le array_*()funzioni.
Outis

3
Hai 3 opzioni: 1: array , 2: User-defined Class , 3: stdClass . Le prestazioni in termini di velocità sono praticamente le stesse durante il confronto arraye un User-defined class(come il tuo ItemType), ma definiti classes tendono a usare meno memoria rispetto all'utilizzo di arrays. stdClassd'altra parte è la più lenta delle tre opzioni e utilizza anche la maggior quantità di memoria.
Andy,

Risposte:


33

Per come la vedo io, dipende da cosa intendi fare successivamente con i dati. Sulla base di alcuni semplici controlli è possibile determinare quale delle due strutture di dati è migliore per te:

  1. Questi dati hanno qualche logica associata?

    Ad esempio, viene $pricememorizzato come un numero intero di centesimi, quindi un prodotto con un prezzo di $ 9,99 avrebbe price = 999e no price = 9.99? (Probabilmente sì) O partNumberdeve corrispondere a una regex specifica? Oppure, devi essere in grado di verificare facilmente se itemCountè disponibile nel tuo inventario? Dovrai svolgere queste funzioni in futuro? In tal caso, la soluzione migliore è creare una classe ora. Ciò significa che è possibile definire vincoli e logica incorporati nella struttura dei dati: private $myPriceè impostato su 999ma $item->getPriceString()restituisce $9.99ed $item->inStock()è disponibile per essere chiamato nell'applicazione.

  2. Passerai questi dati a più funzioni PHP?

    In tal caso, utilizzare una classe. Se stai generando questi dati una volta per eseguire alcune trasformazioni su di esso o semplicemente per inviare come dati JSON a un'altra applicazione (JavaScript o altro), un array è una scelta più semplice. Ma se hai più di due funzioni PHP che accettano questi dati come parametro, usa una classe. Se non altro, ciò ti consente di definire someFunction(MyProductClass $product) {ed è molto chiaro ciò che le tue funzioni si aspettano come input. Man mano che riduci il codice e disponi di più funzioni, sarà molto più semplice sapere quale tipo di dati accetta ciascuna funzione. Vedere someFunction($someArrayData) {non è altrettanto chiaro. Inoltre, ciò non impone la coerenza del tipo e significa che (come hai detto) la struttura flessibile dell'array può causare problemi di sviluppo in un secondo momento

  3. Stai creando una libreria o una base di codice condivisa?

    In tal caso, utilizzare una classe! Pensa a un nuovo sviluppatore che sta utilizzando la tua libreria o un altro sviluppatore da qualche altra parte dell'azienda che non ha mai usato il tuo codice prima. Sarà molto più facile per loro guardare una definizione di classe e capire cosa fa quella classe, o vedere un numero di funzioni nella tua libreria che accettano oggetti di una certa classe, piuttosto che provare a indovinare quale struttura devono generare in un numero di array. Inoltre, questo tocca i problemi di coerenza dei dati con il n. 1: se stai sviluppando una libreria o una base di codice condivisa, sii gentile con i tuoi utenti: dai loro classi che rafforzano la coerenza dei dati e proteggili dal commettere errori nella progettazione dei tuoi dati .

  4. È una piccola parte di un'applicazione o solo una trasformazione di dati? Non rientri in nessuno dei precedenti?

    Una classe potrebbe essere troppo; usa un array se ti va bene e lo trovi più facile. Come accennato in precedenza, se stai semplicemente generando dati strutturati da inviare come JSON o YAML o XML o altro, non preoccuparti di una classe a meno che non sia necessario. Se stai scrivendo un piccolo modulo in un'applicazione più grande e nessun altro modulo / team deve interfacciarsi con il tuo codice, forse un array è sufficiente.

In definitiva, considera le esigenze di ridimensionamento del tuo codice e considera che una matrice strutturata potrebbe essere una soluzione rapida, ma una classe è una soluzione molto più resiliente e scalabile.

Inoltre, considerare quanto segue: se si dispone di una classe e si desidera eseguire l'output su JSON, non vi è alcun motivo per cui non è possibile definire un json_data()metodo della propria classe che restituisca un array di dati JSON-ifiable nella classe. Questo è quello che ho fatto nelle mie applicazioni PHP dove avevo bisogno di inviare dati di classe come JSON. Come esempio:

class Order {
    private $my_total;
    private $my_lineitems;

    public function getItems() { return $this->my_lineitems; }
    public function addItem(Product $p) { $this->my_lineitems[] = $p; }
    public function getTotal() { return $this->my_total; }

    public function forJSON() {
        $items_json = array();
        foreach($this->my_lineitems as $item) $items_json[] = $item->forJSON();
        return array(
            'total' => $this->getTotal(),
            'items' => $items_json
        );
    }
}

$o = new Order();
// do some stuff with it
$json = json_encode($o->forJSON());

Non stai modificando gli elementi di my_lineitems, quindi ottenerli come riferimento non è necessario, soprattutto perché le variabili contengono solo un identificatore per l'oggetto, non l'oggetto stesso. php.net/manual/en/language.oop5.references.php
Chinoto Vokro

Concordato. Penso che fosse uno snippet di un'applicazione molto più grande che aveva bisogno del riferimento per un motivo o qualcosa del genere ...
Josh

2

È possibile implementare la struttura json utilizzando l' interfaccia JsonSerializable e utilizzare la matrice / classe nidificata in qualsiasi modo. La classe è più veloce nelle operazioni get / set e più facile da eseguire il debug. Con Array non hai bisogno di alcuna dichiarazione.

class Order implements \JsonSerializable{
    private $my_total;
    private $my_lineitems;

    public function getItems() { return $this->my_lineitems; }
    public function addItem(Product $p) { $this->my_lineitems[] = $p; }
    public function getTotal() { return $this->my_total; }

    public function jsonSerialize(){
        return [
            'total'=>$this->my_total,
            'products'=>$this->my_lineitems;
        ];
    }
}

class Product implements \JsonSerializable{
    private $name;
    private $price;

    public function jsonSerialize(){ 
        return [
            'name'=>$this->name, 
            'price'=>$this->price
        ];
    }
}

$order = new Order();
$order->addProduct(new Product('Product1', 15));
$order->addProduct(new Product('Product2', 35));
$order->addProduct(new Product('Product3', 42));
$json = json_encode(['order'=>$order, 'username'=>'Eughen']);
/*
json = {
    order: {
        total: 92, 
        products: [
            {
                name: 'Product1',
                price: 15
            }, 
            {
                name: 'Product2',
                price: 35
            },
            {
                name: 'Product3',
                price: 42
            }
        ]
    }, 
    username: 'Eughen'
}
*/

Cosa aggiunge questa domanda alla risposta accettata esistente?
esoterik,

@esoterik nesting. json_encode(['order'=>$o]);in existsing risposta accettato è vuoto: {"order":{}}. Sicuro che puoi usare: $o->forJSON()ogni volta su ogni oggetto ad ogni livello, ma non è davvero un buon design. Perché tutto il tempo è necessario scrivere qualcosa del tipo: $items_json = array(); foreach($this->my_lineitems as $item) $items_json[] = $item->forJSON();
El
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.