PHP: quand utiliser des tableaux et quand utiliser des objets pour des constructions de code stockant principalement des données?

37

PHP est un langage de paradigme mixte, permettant d’utiliser et de retourner des types de données non-objets, tels que des tableaux. Je pose une question pour essayer de clarifier certaines lignes directrices pour la sélection de tableaux par rapport à des objets lors du choix de la structure de programmation à utiliser dans une situation donnée.

Il s’agit en réalité de questions sur les moyens de coder des données à l’aide de constructions en langage PHP et sur la manière de choisir un moyen plutôt que l’autre à des fins de transmission de données (architecture orientée services ou services Web, par exemple).

Exemple

Supposons que vous ayez un type d'élément composé de {coût, nom, numéro de pièce, nombre d'élément}. Votre programme appelle à l'affichage de plusieurs de ces types d'élément, à l'endroit où vous décidez d'utiliser un tableau comme conteneur externe pour contenir chacun des types d'élément. [Vous pouvez également utiliser PHP ArrayObjectpour le paradigme OO, mais ma question ne concerne pas ce tableau (externe)]. Ma question porte sur la manière de coder les données de type d'élément et sur le paradigme à utiliser. PHP vous permet d'utiliser PHP Native Arraysou PHP Objects.

Je peux encoder de telles données, de deux manières ici, comme ceci:

//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
        ),
  );

contre

//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) {..}
}

Ce que je pense

L'encodage de tableau est léger, et plus compatible JSON, mais peut être plus facile à gâcher. Misspell une des clés de tableau associatif et vous pouvez avoir une erreur plus difficile à détecter. Mais il est également plus facile de changer sur un coup de tête. Supposons que je ne souhaite item_countplus stocker de données , je peux utiliser n’importe quel logiciel de traitement de texte pour supprimer facilement toutes les item_countinstances du tableau, puis mettre à jour les autres fonctions qui l’utilisent en conséquence. C'est peut-être un processus plus fastidieux, mais c'est simple.

Le codage orienté objet fait appel aux fonctionnalités des langages IDE et PHP et facilite la détection préalable des erreurs, mais il est en premier lieu plus difficile de programmer et de coder. Je dis plus fort, car vous devez réfléchir un peu sur vos objets, penser à l’avenir, et le codage en sortie d’objet prend une charge cognitive un peu plus lourde que de taper des structures de tableau. Cela dit, une fois codés, certaines modifications item_countseront peut-être plus faciles à mettre en œuvre, dans un sens, la suppression , par exemple, nécessitera de modifier moins de lignes de code. Cependant, les changements eux-mêmes peuvent nécessiter une charge cognitive plus importante par rapport à la méthode de la matrice, car des installations OO de niveau supérieur sont impliquées.

Question

Dans certains cas, cela est clair, comme dans les cas où je devrai manipuler les données. Mais dans certains cas, où je n'ai besoin que de stocker quelques lignes de données "Type d'élément", je ne dispose pas de directives ou de considérations claires sur lesquelles s'appuyer lorsque j'essaie de décider d'utiliser des tableaux ou de construire des objets. Il semble que je peux juste lancer une pièce et en choisir une. est-ce le cas ici?

Dennis
la source
2
Notez que vous pouvez obtenir un objet léger en jetant un tableau à un objet: (object)['foo'=>'bar']. La valeur résultante a class StdClass, sera codée JSON dans sa totalité json_encode()et les propriétés peuvent être renommées aussi facilement que des index de tableau (ce qui n’est pas toujours aussi facile, lorsqu’il est accédé indirectement via une variable). Cependant, il existe différentes opérations sur ces valeurs; Par exemple, vous n'avez pas de unions d'objets comme vous avez des unions de tableaux et vous ne pouvez pas utiliser directement les array_*()fonctions.
outis
3
Vous avez 3 options: 1: array , 2: User-defined Class , 3: stdClass . Les performances en termes de vitesse sont à peu près les mêmes lorsque vous comparez arrayet un User-defined class(tel que votre ItemType), mais les définitions classont tendance à utiliser moins de mémoire que d’utiliser arrays. stdClassd'autre part, est la plus lente des trois options et utilise également le plus de mémoire.
Andy

Réponses:

33

Selon moi, cela dépend de ce que vous avez l'intention de faire avec les données par la suite. Sur la base de quelques vérifications simples, vous pouvez déterminer laquelle des deux structures de données vous convient le mieux:

  1. Est-ce que ces données sont associées à une logique?

    Par exemple, est $pricestocké sous forme de nombre entier de centimes, donc un produit avec un prix de 9,99 $ aurait price = 999et non price = 9.99? (Probablement, oui) Ou partNumberfaut-il correspondre à une expression régulière spécifique? Ou avez-vous besoin de pouvoir vérifier facilement si le itemCountfichier est disponible dans votre inventaire? Aurez-vous besoin de faire ces fonctions à l'avenir? Si c'est le cas, votre meilleur pari est de créer une classe maintenant. Cela signifie que vous pouvez définir des contraintes et une logique intégrées à la structure de données: private $myPriceest défini sur 999mais $item->getPriceString()renvoie $9.99et$item->inStock() est disponible pour être appelé dans votre application.

  2. Allez-vous transmettre ces données à plusieurs fonctions PHP?

    Si c'est le cas, utilisez une classe. Si vous générez ces données une fois pour y effectuer des transformations ou simplement pour envoyer des données JSON à une autre application (JavaScript ou autre), un tableau constitue un choix plus simple. Mais si vous avez plus de deux fonctions PHP qui acceptent ces données en tant que paramètre, utilisez une classe. Si rien d'autre, cela vous permet de définir someFunction(MyProductClass $product) {et il est très clair ce que vos fonctions attendent en entrée. À mesure que votre code évoluera et que vous disposerez de plus de fonctions, il sera beaucoup plus facile de savoir quel type de données chaque fonction accepte. Voir someFunction($someArrayData) {n'est pas aussi clair. De plus, cela n’impose pas la cohérence des types et signifie que (comme vous l’avez dit) la structure flexible du tableau peut causer des problèmes de développement ultérieurement.

  3. Vous construisez une bibliothèque ou une base de code partagée?

    Si oui, utilisez un cours! Pensez à un nouveau développeur qui utilise votre bibliothèque ou à un autre développeur quelque part dans l'entreprise qui n'a jamais utilisé votre code auparavant. Il leur sera beaucoup plus facile d'examiner une définition de classe et de comprendre ce que cette classe fait, ou de voir un certain nombre de fonctions de votre bibliothèque qui acceptent des objets d'une certaine classe, plutôt que d'essayer de deviner de quelle structure elles ont besoin pour générer dans une bibliothèque. nombre de tableaux. Cela concerne également les problèmes de cohérence des données avec # 1: si vous développez une bibliothèque ou une base de code partagé, soyez gentil avec vos utilisateurs: donnez-leur des classes qui renforcent la cohérence des données et évitez qu'ils ne commettent des erreurs dans la conception de vos données. .

  4. Est-ce une petite partie d'une application ou simplement une transformation de données? Ne faites-vous pas partie de ce qui précède?

    Une classe peut être trop; utilisez un tableau si cela vous convient et que vous le trouvez plus facile. Comme indiqué ci-dessus, si vous ne générez que des données structurées à envoyer au format JSON, YAML, XML ou autre, ne vous embêtez pas avec une classe, sauf en cas de nécessité. Si vous écrivez un petit module dans une application plus grande et qu'aucun autre module / équipe n'a besoin d'interface avec votre code, un tableau suffit peut-être.

En fin de compte, prenez en compte les besoins en mise à l'échelle de votre code et considérez qu'un tableau structuré peut constituer une solution rapide, mais une classe est une solution beaucoup plus résiliente et évolutive.

Prenez également en compte les éléments suivants: si vous avez une classe et que vous souhaitez json_data()exporter au format JSON, rien ne vous empêche de définir une méthode de votre classe renvoyant un tableau JSON-ifiable des données de la classe. C'est ce que j'ai fait dans mes applications PHP où je devais envoyer des données de classe au format JSON. Par exemple:

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());
Josh
la source
Vous ne modifiez pas les éléments de my_lineitems, il est donc inutile de les obtenir par référence, d'autant plus que les variables ne contiennent qu'un identifiant pour l'objet, pas l'objet lui-même. php.net/manual/en/language.oop5.references.php
Chinoto Vokro
D'accord. Je pense que c'était un extrait d'une application beaucoup plus volumineuse qui nécessitait la référence pour une raison ou quelque chose du genre ...
Josh
2

Vous pouvez implémenter la structure json en utilisant l' interface JsonSerializable et utiliser un tableau / classe imbriqué de quelque manière que ce soit. La classe est plus rapide dans les opérations get / set et plus facile à déboguer. Avec Array, vous n'avez besoin d'aucune déclaration.

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'
}
*/
El '
la source
Qu'est-ce que cette question ajoute à la réponse acceptée existante?
esoterik
@esoterik nid. json_encode(['order'=>$o]);dans existsing réponse acceptée est vide: {"order":{}}. Bien sûr, vous pouvez l’utiliser: à $o->forJSON()chaque fois sur tous les objets, à tous les niveaux, mais son design n’est pas vraiment bon. Parce que tout le temps il faut écrire quelque chose comme: $items_json = array(); foreach($this->my_lineitems as $item) $items_json[] = $item->forJSON();
El '10