PHP: коли використовувати масиви та коли використовувати об'єкти для конструкцій коду, що зберігають дані?


37

PHP - це змішана мова парадигми, що дозволяє використовувати та повертати необ'єктивні типи даних, наприклад масиви. Я ставлю запитання, щоб спробувати уточнити деякі вказівки щодо вибору масивів проти об’єктів, вирішуючи, яку програмування побудувати для конкретної ситуації.

Це справді питання про способи кодування даних за допомогою конструкцій мови PHP та коли з більшою ймовірністю можна вибрати один із інших для цілей передачі даних (тобто, орієнтована на сервісну архітектуру чи веб-сервіси).

Приклад

Припустимо, у вас є тип предмета, що складається з {вартість, ім'я, номер_посередництва, номер_об'єкта}. Ваша програма вимагає відображення декількох таких типів елементів, до яких ви вирішите використовувати масив як зовнішній контейнер для зберігання кожного з типів елементів. [Ви також можете використовувати 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 та полегшує заздалегідь зафіксування будь-яких помилок, але важче програмувати та кодувати в першу чергу. Я говорю важче, тому що ви повинні трохи подумати над своїми об'єктами, заздалегідь задумайтеся, і кодування OO займає трохи більшу пізнавальну навантаження, ніж введення структур масивів. Однак, після того, як це закодовано, деякі зміни, можливо, простіше здійснити, в певному сенсі, що для видалення item_count, наприклад, потрібно буде змінити менше рядків коду. Але самі зміни все ж можуть вимагати більшого когнітивного навантаження порівняно з методом масиву, оскільки задіяні засоби вищого рівня.

Питання

У деяких випадках це зрозуміло, як у випадках, коли мені потрібно буде виконати маніпуляції над даними. Але в деяких випадках, коли мені потрібно просто зберегти кілька рядків даних "Тип предмета", я не маю чітких вказівок або міркувань, на яких слід спиратися, чи потрібно використовувати масиви чи будувати об'єкти. Здається, я можу просто кинути монету і забрати її. Це так?


2
Зверніть увагу , ви можете отримати легкий об'єкт лиття масиву до об'єкту: (object)['foo'=>'bar']. Отримане значення має клас StdClass, буде кодовано JSON у повному обсязі, json_encode()а властивості можна перейменовувати так само легко, як і індекси масиву (що не завжди так просто, коли до нього можна отримати опосередковано через змінну). Однак існують різні операції з такими значеннями; наприклад, у вас немає об'єктних об'єднань, оскільки у вас є об'єднання масивів, і ви не можете безпосередньо використовувати array_*()функції.
outis

3
У вас є 3 варіанти: 1: array , 2: User-defined Class , 3: stdClass . Продуктивність за швидкістю майже однакова при порівнянні arrayта 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
Chinoto Vokro

Домовились. Я думаю, що це був фрагмент набагато більшого додатку, який потребував посилання з причини, чи щось ...
Josh,

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();
El
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.