PHP: quando usar matrizes e quando usar objetos para construções de código que armazenam principalmente dados?


37

PHP é uma linguagem de paradigma mista, permitindo usar e retornar tipos de dados que não são objetos, como matrizes. Faço uma pergunta para tentar esclarecer algumas diretrizes para a seleção de matrizes versus objetos ao decidir sobre qual construção de programação usar em uma situação específica.

Esta é realmente uma pergunta sobre maneiras de codificar dados usando construções de linguagem PHP e quando é mais provável que uma maneira seja escolhida sobre outra para fins de passagem de dados (por exemplo, Arquitetura Orientada a Serviços ou serviços da Web).

Exemplo

Suponha que você tenha um tipo de item que consiste em {cost, name, part_number, item_count}. Seu programa solicita a exibição de vários tipos de itens, para onde você decide usar uma matriz como um contêiner externo para armazenar cada um dos tipos de itens. [Você também pode usar PHP ArrayObjectpara o paradigma OO, mas minha pergunta não é sobre esse array (externo)]. Minha pergunta é sobre como codificar os dados do tipo de item e sobre qual paradigma usar. PHP permite que você use PHP Native Arraysou PHP Objects.

Eu posso codificar esses dados, de duas maneiras aqui, assim:

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

O que eu estou pensando

A codificação de matriz é leve e mais preparada para JSON, mas pode ser mais fácil atrapalhar. Escreva incorretamente uma das chaves associativas da matriz e você poderá ter um erro mais difícil de detectar. Mas também é mais fácil mudar por capricho. Digamos que não queira item_countmais armazenar , posso usar qualquer software de processamento de texto para remover facilmente todas as item_countinstâncias da matriz e atualizar outras funções que a usem adequadamente. Pode ser um processo mais tedioso, mas é simples.

A codificação orientada a objetos chama os recursos de linguagem IDE e PHP e facilita a detecção de erros com antecedência, mas é mais difícil programar e codificar em primeiro lugar. Digo mais, porque você precisa pensar um pouco sobre seus objetos, pensar adiante, e a codificação OO requer uma carga cognitiva um pouco maior do que digitar estruturas de array. Dito isto, uma vez codificadas, algumas alterações talvez sejam mais fáceis de implementar, de certo modo, que remover item_count, por exemplo, exigirá a alteração de menos linhas de código. Mas as mudanças em si ainda podem exigir uma carga cognitiva mais alta em comparação com o método de matriz, uma vez que estão envolvidas instalações de OO de nível superior.

Questão

Em alguns casos, é claro, como casos em que precisarei executar manipulações nos dados. Mas, em alguns casos, onde eu preciso armazenar apenas algumas linhas de dados "Tipo de item", não tenho diretrizes ou considerações claras para apoiar ao tentar decidir se deve usar matrizes ou construir objetos. Parece que posso simplesmente jogar uma moeda e escolher uma. É esse o caso aqui?


2
Nota você pode obter um objeto leve por lançando uma matriz para um objeto: (object)['foo'=>'bar']. O valor resultante tem classe StdClass, será codificado em JSON por completo json_encode()e as propriedades podem ser renomeadas tão facilmente quanto os índices da matriz (o que nem sempre é tão fácil, quando é acessado indiretamente por meio de uma variável). No entanto, existem operações diferentes em tais valores; por exemplo, você não possui uniões de objetos como uniões de matriz e não pode usar diretamente as array_*()funções.
out

3
Você tem 3 opções: 1: array , 2: User-defined Class , 3: stdClass . O desempenho em termos de velocidade é praticamente o mesmo ao comparar arraye a User-defined class(como o seu ItemType), mas os classes definidos tendem a usar menos memória do que o arrays. stdClasspor outro lado, é a mais lenta das três opções e também usa mais memória.
Andy

Respostas:


33

A meu ver, isso depende do que você pretende fazer com os dados posteriormente. Com base em algumas verificações simples, você pode determinar qual das duas estruturas de dados é melhor para você:

  1. Esses dados têm alguma lógica associada a eles?

    Por exemplo, é $pricearmazenado como um número inteiro de centavos, então um produto com um preço de US $ 9,99 teria price = 999e não price = 9.99? (Provavelmente, sim) Ou partNumberprecisa corresponder a um regex específico? Ou você precisa verificar facilmente se o itemCountitem está disponível no seu inventário? Você precisará executar essas funções no futuro? Nesse caso, sua melhor aposta é criar uma classe agora. Isso significa que você pode definir restrições e lógica incorporadas à estrutura de dados: private $myPriceestá definido como 999mas $item->getPriceString()retorna $9.99e $item->inStock()está disponível para ser chamado em seu aplicativo.

  2. Você vai passar esses dados para várias funções PHP?

    Se sim, então use uma classe. Se você estiver gerando esses dados uma vez para executar algumas transformações ou apenas enviar como dados JSON para outro aplicativo (JavaScript ou outro), uma matriz é uma escolha mais fácil. Mas se você tiver mais de duas funções PHP que aceitam esses dados como parâmetro, use uma classe Se nada mais, isso permite que você defina someFunction(MyProductClass $product) {e é muito claro o que suas funções esperam como entrada. À medida que você expande seu código e tem mais funções, será muito mais fácil saber que tipo de dados cada função aceita. Ver someFunction($someArrayData) {não é tão claro. Além disso, isso não impõe consistência de tipo e significa que (como você disse) a estrutura flexível da matriz pode causar problemas no desenvolvimento posteriormente.

  3. Você está construindo uma biblioteca ou uma base de códigos compartilhada?

    Se sim, use uma classe! Pense em algum novo desenvolvedor que esteja usando sua biblioteca ou em outro lugar da empresa que nunca usou seu código antes. Será muito mais fácil para eles olhar para uma definição de classe e entender o que essa classe faz, ou ver várias funções em sua biblioteca que aceitam objetos de uma determinada classe, do que tentar adivinhar qual estrutura elas precisam gerar em um número de matrizes. Além disso, isso aborda os problemas de consistência de dados com o número 1: se você estiver desenvolvendo uma biblioteca ou uma base de códigos compartilhada, seja gentil com os usuários: dê-lhes classes que reforcem a consistência dos dados e os protejam de cometer erros no design dos dados. .

  4. Isso é uma pequena parte de um aplicativo ou apenas uma transformação de dados? Você não se encaixa em nenhuma das opções acima?

    Uma classe pode ser demais; use uma matriz se for mais conveniente e você achar mais fácil. Como mencionado acima, se você está apenas gerando dados estruturados para enviar como JSON ou YAML ou XML ou qualquer outra coisa, não se preocupe com uma classe, a menos que seja necessário. Se você estiver escrevendo um módulo pequeno em um aplicativo maior e nenhum outro módulo / equipe precisar interagir com seu código, talvez uma matriz seja suficiente.

Por fim, considere as necessidades de dimensionamento do seu código e considere que uma matriz estruturada pode ser uma solução rápida, mas uma classe é uma solução muito mais resiliente e escalável.

Além disso, considere o seguinte: se você tem uma classe e deseja gerar para JSON, não há razão para não definir um json_data()método da sua classe que retorne uma matriz de dados passíveis de JSON na classe. Foi o que fiz em meus aplicativos PHP, onde eu precisava enviar dados de classe como JSON. Como um exemplo:

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

Você não está alterando os elementos de my_lineitems, portanto, obtê-los por referência é desnecessário, especialmente porque as variáveis ​​mantêm apenas um identificador para o objeto, não para o próprio objeto. php.net/manual/en/language.oop5.references.php
Chinoto Vokro

Acordado. Acho que foi um trecho de uma aplicação muito maior que a necessária a referência para uma razão, ou algo assim ...
Josh

2

Você pode implementar a estrutura json usando a interface JsonSerializable e usar matriz / classe aninhada de qualquer maneira. A classe é mais rápida nas operações de obtenção / configuração e mais fácil de depurar. Com o Array, você não precisa de declaração.

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

O que essa pergunta acrescenta que a resposta aceita existente?
esoterik 4/18

@esoterik nesting. json_encode(['order'=>$o]);em existsing resposta aceita é em branco: {"order":{}}. Claro que você pode usar: $o->forJSON()sempre em todos os objetos em todos os níveis, mas não é um design realmente bom. Porque todo o tempo você precisa escrever algo como: $items_json = array(); foreach($this->my_lineitems as $item) $items_json[] = $item->forJSON();
El
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.