PHP: когда использовать массивы и когда использовать объекты для конструкций кода, в основном для хранения данных?

37

PHP - это язык смешанной парадигмы, позволяющий использовать и возвращать необъектные типы данных, такие как массивы. Я задаю вопрос, чтобы попытаться прояснить некоторые рекомендации по выбору массивов и объектов при принятии решения о том, какую программную конструкцию использовать в конкретной ситуации.

Это действительно вопрос о способах кодирования данных с использованием языковых конструкций PHP и о том, когда один путь должен быть более вероятен, чем другой для целей передачи данных (т. Е. Сервис-ориентированная архитектура или веб-сервисы).

пример

Предположим, у вас есть тип элемента, состоящий из {cost, name, part_number, item_count}. Ваша программа требует отображения нескольких таких типов элементов, чтобы вы решили использовать массив в качестве внешнего контейнера для хранения каждого из типов элементов. [Вы также можете использовать PHP ArrayObjectдля ОО-парадигмы, но мой вопрос не об этом (внешнем) массиве]. Мой вопрос о том, как кодировать данные типа элемента и какую парадигму использовать. PHP позволяет использовать PHP Native Arraysили PHP Objects.

Я могу закодировать такие данные двумя способами, вот так:

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

против

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

О чем я думаю

Кодирование массива облегчено и более готово к JSON, но может быть проще испортить. Неправильно написан один из ключей ассоциативного массива, и может возникнуть ошибка, которую сложнее отследить. Но это также легче изменить по прихоти. Скажем, я не хочу item_countбольше хранить , я могу использовать любое программное обеспечение для обработки текста, чтобы легко удалить все item_countэкземпляры в массиве и затем обновить другие функции, которые используют его соответствующим образом. Это может быть более утомительный процесс, но это просто.

Объектно-ориентированное кодирование требует использования языковых возможностей IDE и PHP и облегчает обнаружение любых ошибок заранее, но в первую очередь сложнее для программирования и кодирования. Я говорю сложнее, потому что вы должны немного подумать о своих объектах, подумать заранее, и ОО-кодирование требует немного большей когнитивной нагрузки, чем типизация структур массива. Тем не менее, после его кодирования некоторые изменения могут быть легче реализованы, в том смысле, что удаление item_count, например, потребует изменения меньшего количества строк кода. Но сами изменения могут по-прежнему требовать более высокой когнитивной нагрузки по сравнению с методом массива, так как задействованы средства OO более высокого уровня.

Вопрос

В некоторых случаях это понятно, например, в случаях, когда мне нужно будет выполнять манипуляции с данными. Но в некоторых случаях, когда мне нужно просто сохранить несколько строк данных «Тип элемента», у меня нет четких рекомендаций или соображений, на которые следует опираться при попытке решить, использовать ли массивы или создавать объекты. Кажется, я могу просто бросить монетку и выбрать одну. Это тот случай здесь?

Деннис
источник
2
Обратите внимание , вы можете получить легкий объект литья массива к объекту: (object)['foo'=>'bar']. Полученное значение имеет класс StdClass, будет полностью закодировано в JSON-коде, json_encode()и свойства можно будет переименовывать так же легко, как индексы массива (что не всегда так просто, когда к нему обращаются косвенно через переменную). Однако существуют разные операции с такими значениями; например, у вас нет объединений объектов, так как у вас есть объединения массивов, и вы не можете напрямую использовать array_*()функции.
августа
3
У вас есть 3 варианта: 1: array , 2: User-defined Class , 3: stdClass . Производительность с точки зрения скорости почти одинакова при сравнении arrayи a User-defined class(например, вашего ItemType), но определенные classes, как правило, используют меньше памяти, чем arrays. stdClassс другой стороны, это самый медленный из трех вариантов, который также использует больше всего памяти.
Энди

Ответы:

33

То, как я это вижу, зависит от того, что вы собираетесь делать с данными впоследствии. Основываясь на нескольких простых проверках, вы можете определить, какая из двух структур данных вам подходит:

  1. Есть ли у этих данных какая-либо логика, связанная с этим?

    Например, $priceхранится как целое число центов, так что продукт с ценой в $ 9,99 будет иметь, price = 999а не price = 9.99? (Возможно, да) Или partNumberнужно соответствовать определенному регулярному выражению? Или вам нужно легко проверить itemCountналичие в вашем инвентаре? Вам нужно будет выполнить эти функции в будущем? Если это так, то лучше всего создать класс сейчас. Это означает, что вы можете определить ограничения и логику, встроенные в структуру данных: private $myPriceустановлено в значение, 999но $item->getPriceString()возвращается $9.99и $item->inStock()доступно для вызова в вашем приложении.

  2. Вы собираетесь передавать эти данные нескольким функциям PHP?

    Если так, то используйте класс. Если вы генерируете эти данные один раз для выполнения каких-либо преобразований или просто для отправки данных в формате JSON в другое приложение (JavaScript или другое), тогда массив будет более легким выбором. Но если у вас есть более двух функций PHP, которые принимают эти данные в качестве параметра, используйте класс. Если ничего другого, это позволяет вам определить, someFunction(MyProductClass $product) {и очень ясно, что ваши функции ожидают в качестве ввода. По мере того, как вы масштабируете свой код и получаете больше функций, будет намного легче узнать, какой тип данных принимает каждая функция. Видеть someFunction($someArrayData) {не так ясно. Кроме того, это не обеспечивает согласованность типов и означает, что (как вы сказали) гибкая структура массива может позже вызвать боль при разработке

  3. Вы строите библиотеку или базу общего кода?

    Если это так, используйте класс! Подумайте о каком-то новом разработчике, который использует вашу библиотеку, или о другом разработчике в компании, который никогда раньше не использовал ваш код. Им будет гораздо проще взглянуть на определение класса и понять, что делает этот класс, или увидеть ряд функций в вашей библиотеке, которые принимают объекты определенного класса, чем пытаться угадать, какую структуру им нужно создать в количество массивов. Кроме того, это касается проблем согласованности данных с № 1: если вы разрабатываете библиотеку или базу общего кода, будьте добры к своим пользователям: предоставьте им классы, которые обеспечивают согласованность данных и защитят их от ошибок при проектировании ваших данных ,

  4. Это небольшая часть приложения или просто преобразование данных? Вы не вписываетесь ни в один из вышеперечисленных?

    Класс может быть слишком много; используйте массив, если он вам подходит, и вам будет проще. Как упоминалось выше, если вы просто генерируете структурированные данные для отправки в формате JSON, YAML, XML или чего-либо другого, не беспокойтесь о классе, если в этом нет необходимости. Если вы пишете небольшой модуль в более крупном приложении, и никакие другие модули / группы не должны взаимодействовать с вашим кодом, возможно, достаточно массива.

В конечном счете, рассмотрите потребности вашего кода в масштабировании и подумайте, что структурированный массив может быть быстрым решением, но класс является гораздо более гибким и масштабируемым решением.

Также учтите следующее: если у вас есть класс, и вы хотите вывести его в JSON, нет причины, по которой вы не можете определить json_data()метод вашего класса, который возвращает JSON-ifiable массив данных в классе. Это то, что я делал в своих приложениях PHP, где мне нужно было отправлять данные классов в формате JSON. В качестве примера:

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());
мистифицировать
источник
Вы не изменяете элементы my_lineitems, поэтому получать их по ссылке не нужно, тем более что переменные содержат только идентификатор объекта, а не сам объект. php.net/manual/en/language.oop5.references.php
Чиното Вокро
Согласовано. Я думаю, что это был фрагмент гораздо более крупного приложения, для которого нужна ссылка по какой-то причине, или что-то в этом роде
Джош
2

Вы можете реализовать структуру json с помощью интерфейса JsonSerializable и использовать вложенный массив / класс любым способом. Класс быстрее в операциях get / set и легче в отладке. С Array вам не нужно декларировать.

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»
источник
Что этот вопрос добавляет, что существующий принятый ответ?
esoterik
@esoterik вложения. json_encode(['order'=>$o]);в existsing общепринятого ответ пустой: {"order":{}}. Конечно, вы можете использовать: $o->forJSON()каждый раз на каждом объекте на каждом уровне, но это не очень хороший дизайн. Потому что все время нужно писать что-то вроде: $items_json = array(); foreach($this->my_lineitems as $item) $items_json[] = $item->forJSON();
Эль