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

Что этот вопрос добавляет, что существующий принятый ответ?
— esoterik

@esoterik вложения. json_encode(['order'=>$o]);в existsing общепринятого ответ пустой: {"order":{}}. Конечно, вы можете использовать: $o->forJSON()каждый раз на каждом объекте на каждом уровне, но это не очень хороший дизайн. Потому что все время нужно писать что-то вроде: $items_json = array(); foreach($this->my_lineitems as $item) $items_json[] = $item->forJSON();
— Эль
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.