PHP: ¿cuándo usar matrices y cuándo usar objetos para construcciones de código que almacenan principalmente datos?


37

PHP es un lenguaje de paradigma mixto, que permite usar y devolver tipos de datos que no son objetos, como las matrices. Planteo una pregunta para tratar de aclarar algunas pautas para la selección de matrices frente a objetos al decidir qué construcción de programación usar en una situación particular.

Esta es realmente una pregunta sobre las formas de codificar datos usando construcciones de lenguaje PHP y cuándo una forma es más probable que se elija sobre otra para fines de paso de datos (es decir, Arquitectura Orientada a Servicios o servicios web).

Ejemplo

Supongamos que tiene un tipo de elemento que consiste en {costo, nombre, número de parte, número de elemento}. Su programa requiere la visualización de varios de estos tipos de elementos, donde decide utilizar una matriz como contenedor externo para contener cada uno de los tipos de elementos. [También puede usar PHP ArrayObjectpara el paradigma OO, pero mi pregunta no es sobre esa matriz (externa)]. Mi pregunta es sobre cómo codificar los datos de tipo de elemento y sobre qué paradigma usar. PHP te permite usar PHP Native Arrayso PHP Objects.

Puedo codificar tales datos, de dos maneras aquí, así:

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

Lo que estoy pensando

La codificación de matriz es liviana y está más lista para JSON, pero puede ser más fácil de estropear. Escribe mal una de las claves de la matriz asociativa y es posible que tengas un error que sea más difícil de detectar. Pero también es más fácil cambiar por capricho. Digamos que no quiero almacenar item_countmás, puedo usar cualquier software de procesamiento de texto para eliminar fácilmente todas las item_countinstancias en la matriz y luego actualizar otras funciones que lo usan en consecuencia. Puede ser un proceso más tedioso, pero es simple.

La codificación orientada a objetos recurre a las instalaciones de lenguaje IDE y PHP y hace que sea más fácil detectar cualquier error de antemano, pero es más difícil de programar y codificar en primer lugar. Digo más, porque tienes que pensar un poco sobre tus objetos, pensar con anticipación, y la codificación OO requiere una carga cognitiva un poco más alta que escribir estructuras de matriz. Dicho esto, una vez que está codificado, algunos cambios pueden ser más fáciles de implementar, en cierto sentido, que eliminar item_count, por ejemplo, requerirá cambiar menos líneas de código. Pero los cambios en sí mismos aún pueden requerir una mayor carga cognitiva en comparación con el método de matriz, ya que están involucradas las instalaciones de OO de nivel superior.

Pregunta

En algunos casos está claro, como los casos en los que tendré que realizar manipulaciones en los datos. Pero en algunos casos, donde solo necesito almacenar unas pocas líneas de datos de "Tipo de elemento", no tengo pautas o consideraciones claras en las que apoyarme cuando intento decidir si usar matrices o si construir objetos. Parece que puedo lanzar una moneda y elegir una. ¿Es ese el caso aquí?


2
Nota usted puede conseguir un objeto ligero echando una matriz a un objeto: (object)['foo'=>'bar']. El valor resultante tiene clase StdClass, estará codificado en JSON en su totalidad por json_encode()y las propiedades se pueden renombrar tan fácilmente como índices de matriz (que no siempre es tan fácil, cuando se accede indirectamente a través de una variable). Sin embargo, hay diferentes operaciones en tales valores; por ejemplo, no tiene uniones de objetos ya que tiene uniones de matriz, y no puede usar directamente las array_*()funciones.
outis

3
Tiene 3 opciones: 1: array , 2: User-defined Class , 3: stdClass . El rendimiento en términos de velocidad es más o menos el mismo cuando se compara arrayy a User-defined class(como su ItemType), pero los classes definidos tienden a usar menos memoria que los arrays. stdClassPor otro lado, es la más lenta de las tres opciones y también utiliza la mayor cantidad de memoria.
Andy

Respuestas:


33

A mi modo de ver, depende de lo que intente hacer con los datos después. En función de algunas comprobaciones simples, puede determinar cuál de las dos estructuras de datos es mejor para usted:

  1. ¿Estos datos tienen alguna lógica asociada?

    Por ejemplo, se $pricealmacena como un número entero de centavos, por lo que un producto con un precio de $ 9.99 tendría price = 999y no price = 9.99? (Probablemente, sí) ¿O partNumbernecesita coincidir con una expresión regular específica? O, ¿necesita poder verificar fácilmente si itemCountestá disponible en su inventario? ¿Tendrá que hacer estas funciones en el futuro? Si es así, entonces su mejor apuesta es crear una clase ahora. Esto significa que puede definir restricciones y lógica integradas en la estructura de datos: private $myPricese establece en 999pero $item->getPriceString()devuelve $9.99y $item->inStock()está disponible para ser llamado en su aplicación.

  2. ¿Va a pasar estos datos a múltiples funciones de PHP?

    Si es así, usa una clase. Si está generando estos datos una vez para realizar algunas transformaciones en ellos, o simplemente para enviarlos como datos JSON a otra aplicación (JavaScript o no), entonces una matriz es una opción más fácil. Pero si tiene más de dos funciones PHP que aceptan estos datos como parámetro, use una clase. Por lo menos, eso le permite definir someFunction(MyProductClass $product) {y queda muy claro qué esperan sus funciones como entrada. A medida que amplíe su código y tenga más funciones, será mucho más fácil saber qué tipo de datos acepta cada función. Ver someFunction($someArrayData) {no es tan claro. Además, esto no exige la coherencia de los tipos y significa que (como dijiste) la estructura flexible de la matriz puede causar dolor de desarrollo más adelante

  3. ¿Estás construyendo una biblioteca o base de código compartido?

    Si es así, ¡ usa una clase! Piense en algún desarrollador nuevo que esté usando su biblioteca, u otro desarrollador en otro lugar de la compañía que nunca haya usado su código antes. Será mucho más fácil para ellos mirar una definición de clase y comprender lo que hace esa clase, o ver una serie de funciones en su biblioteca que aceptan objetos de una clase determinada, que tratar de adivinar qué estructura necesitan generar en un Número de matrices. Además, esto toca los problemas de consistencia de datos con el n. ° 1: si está desarrollando una biblioteca o base de código compartido, sea amable con sus usuarios: deles clases que refuercen la consistencia de los datos y los proteja de cometer errores con el diseño de sus datos .

  4. ¿Es esta una pequeña parte de una aplicación o simplemente una transformación de datos? ¿No encajas en ninguno de los anteriores?

    Una clase puede ser demasiado; use una matriz si le conviene y le resulta más fácil. Como se mencionó anteriormente, si solo está generando datos estructurados para enviar como JSON o YAML o XML o lo que sea, no se moleste con una clase a menos que sea necesario. Si está escribiendo un módulo pequeño en una aplicación más grande, y ningún otro módulo / equipo necesita interactuar con su código, tal vez una matriz sea suficiente.

En última instancia, considere las necesidades de escalado de su código y considere que una matriz estructurada podría ser una solución rápida, pero una clase es una solución mucho más resistente y escalable.

Además, tenga en cuenta lo siguiente: si tiene una clase y desea exportar a JSON, no hay ninguna razón por la que no pueda definir un json_data()método de su clase que devuelva una matriz JSON de los datos de la clase. Esto es lo que hice en mis aplicaciones PHP donde necesitaba enviar datos de clase como JSON. Como ejemplo:

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());

No está cambiando los elementos de my_lineitems, por lo que obtenerlos por referencia es innecesario, especialmente porque las variables solo contienen un identificador para el objeto, no el objeto en sí. php.net/manual/en/language.oop5.references.php
Chinoto Vokro

Convenido. Creo que fue un fragmento de una aplicación mucho más grande que necesitaba la referencia por una razón, o algo así ...
Josh

2

Puede implementar la estructura json usando la interfaz JsonSerializable y usar una matriz / clase anidada de cualquier manera. La clase es más rápida en operaciones get / set y más fácil de depurar. Con Array no necesitas declaración.

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'
}
*/

¿Qué agrega esta pregunta que la respuesta aceptada existente?
esoterik

@esoterik anidando. json_encode(['order'=>$o]);existsing en respuesta aceptada está en blanco: {"order":{}}. Claro que puede usar: $o->forJSON()cada vez en cada objeto en cada nivel, pero no es un diseño realmente bueno. Porque todo el tiempo necesitas escribir algo como: $items_json = array(); foreach($this->my_lineitems as $item) $items_json[] = $item->forJSON();
El '
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.