PHP: khi nào nên sử dụng mảng và khi nào nên sử dụng các đối tượng cho các cấu trúc mã lưu trữ dữ liệu chủ yếu?


37

PHP là một ngôn ngữ mô hình hỗn hợp, cho phép sử dụng và trả về các kiểu dữ liệu không phải đối tượng, chẳng hạn như mảng. Tôi đặt ra một câu hỏi để cố gắng làm rõ một số hướng dẫn để lựa chọn mảng so với các đối tượng khi quyết định sử dụng cấu trúc lập trình nào để sử dụng trong một tình huống cụ thể.

Đây thực sự là một câu hỏi về cách mã hóa dữ liệu bằng các cấu trúc ngôn ngữ PHP và khi một cách có nhiều khả năng được chọn hơn một cách khác cho mục đích truyền dữ liệu (ví dụ: Kiến trúc hướng dịch vụ hoặc dịch vụ web).

Thí dụ

Giả sử bạn có một loại mặt hàng bao gồm {chi phí, tên, part_number, item_count}. Chương trình của bạn yêu cầu hiển thị một số loại mặt hàng như vậy, đến nơi bạn quyết định sử dụng một mảng làm thùng chứa bên ngoài để giữ từng loại mặt hàng. [Bạn cũng có thể sử dụng PHP là ArrayObjectcho OO mô hình, nhưng câu hỏi của tôi không phải là về đó (bên ngoài) mảng]. Câu hỏi của tôi là về cách mã hóa dữ liệu loại mặt hàng và về mô hình sử dụng. PHP cho phép bạn sử dụng PHP Native Arrayshoặc PHP Objects.

Tôi có thể mã hóa dữ liệu đó, theo hai cách ở đây, như vậy:

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

đấu với

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

Tôi đang nghĩ gì

Mã hóa mảng có trọng lượng nhẹ và sẵn sàng JSON hơn, nhưng có thể dễ gây rối hơn. Bỏ lỡ một trong các khóa mảng kết hợp và bạn có thể gặp lỗi khó bắt hơn. Nhưng nó cũng dễ dàng hơn để thay đổi bất chợt. Nói rằng tôi không muốn lưu trữ item_countnữa, tôi có thể sử dụng bất kỳ phần mềm xử lý văn bản nào để dễ dàng xóa tất cả các item_countphiên bản trong mảng và sau đó cập nhật các chức năng khác sử dụng nó cho phù hợp. Nó có thể là một quá trình tẻ nhạt hơn, nhưng nó đơn giản.

Mã hóa hướng đối tượng gọi các phương tiện ngôn ngữ IDE và PHP và giúp dễ dàng bắt được bất kỳ lỗi nào trước đó, nhưng khó lập trình và mã hóa ngay từ đầu. Tôi nói khó hơn, bởi vì bạn phải suy nghĩ một chút về các đối tượng của mình, suy nghĩ trước và mã hóa OO có tải nhận thức cao hơn một chút so với việc gõ các cấu trúc mảng. Điều đó nói rằng, một khi nó được mã hóa, một số thay đổi có thể dễ thực hiện hơn, theo một nghĩa nào đó, việc xóa item_count, chẳng hạn, sẽ yêu cầu thay đổi ít dòng mã hơn. Nhưng bản thân các thay đổi vẫn có thể yêu cầu tải nhận thức cao hơn so với phương pháp mảng, vì các cơ sở OO cấp cao hơn có liên quan.

Câu hỏi

Trong một số trường hợp thì rõ ràng, giống như các trường hợp tôi sẽ cần phải thực hiện các thao tác trên dữ liệu. Nhưng trong một số trường hợp, khi tôi chỉ cần lưu trữ một vài dòng dữ liệu "Loại vật phẩm", tôi không có hướng dẫn hoặc cân nhắc rõ ràng để dựa vào khi cố gắng quyết định nên sử dụng mảng hay có nên xây dựng các đối tượng hay không. Có vẻ như tôi chỉ có thể tung đồng xu và chọn một đồng. Có phải đó là trường hợp ở đây?


2
Lưu ý bạn có thể lấy một đối tượng nhẹ bằng cách truyền một mảng tới một đối tượng : (object)['foo'=>'bar']. Giá trị kết quả có lớp StdClass, sẽ được mã hóa toàn bộ JSON json_encode()và các thuộc tính có thể được đổi tên dễ dàng như các chỉ số mảng (không phải lúc nào cũng dễ dàng, khi nó được truy cập gián tiếp thông qua một biến). Tuy nhiên, có các hoạt động khác nhau trên các giá trị đó; ví dụ: bạn không có các hiệp hội đối tượng vì bạn có các hiệp hội mảng và bạn không thể trực tiếp sử dụng các array_*()hàm.
outis

3
Bạn có 3 tùy chọn: 1 : array , 2 : User-defined Class , 3 : stdClass . Hiệu suất về tốc độ khá giống nhau khi so sánh arrayvà một User-defined class(chẳng hạn như của bạn ItemType), nhưng classes được xác định có xu hướng sử dụng ít bộ nhớ hơn so với sử dụng arrays. stdClassmặt khác là chậm nhất trong ba tùy chọn và cũng sử dụng nhiều bộ nhớ nhất.
Andy

Câu trả lời:


33

Cách tôi nhìn thấy điều này, nó phụ thuộc vào những gì bạn định làm với dữ liệu sau đó. Dựa trên một vài kiểm tra đơn giản, bạn có thể xác định cấu trúc dữ liệu nào trong hai cấu trúc dữ liệu tốt hơn cho mình:

  1. Dữ liệu này có logic nào liên quan đến nó không?

    Ví dụ: được $pricelưu trữ dưới dạng số xu nguyên, vậy một sản phẩm có giá 9,99 đô la sẽ có price = 999và không price = 9.99? (Có lẽ, có) Hoặc không partNumbercần phải khớp với một regex cụ thể? Hoặc, bạn có cần phải dễ dàng kiểm tra nếu itemCountcó sẵn trong kho của bạn không? Bạn sẽ cần phải làm những chức năng này trong tương lai? Nếu vậy, đặt cược tốt nhất của bạn là tạo một lớp ngay bây giờ. Điều này có nghĩa là bạn có thể xác định các ràng buộc và logic được tích hợp trong cấu trúc dữ liệu: private $myPriceđược đặt thành 999nhưng $item->getPriceString()trả về $9.99$item->inStock()có sẵn để được gọi trong ứng dụng của bạn.

  2. Bạn có định truyền dữ liệu này cho nhiều hàm PHP không?

    Nếu vậy, sau đó sử dụng một lớp. Nếu bạn đang tạo dữ liệu này một lần để thực hiện một số biến đổi trên dữ liệu đó hoặc chỉ để gửi dưới dạng dữ liệu JSON đến một ứng dụng khác (JavaScript hoặc cách khác) thì một mảng là một lựa chọn dễ dàng hơn. Nhưng nếu bạn có nhiều hơn hai hàm PHP chấp nhận dữ liệu này làm tham số, hãy sử dụng một lớp. Nếu không có gì khác, điều đó cho phép bạn xác định someFunction(MyProductClass $product) {và rất rõ chức năng của bạn mong đợi là đầu vào. Khi bạn mở rộng mã của mình và có nhiều chức năng hơn, sẽ dễ dàng hơn nhiều để biết loại dữ liệu mà mỗi chức năng chấp nhận. Nhìn thấy someFunction($someArrayData) {gần như không rõ ràng. Ngoài ra, điều này không thực thi tính nhất quán của loại và có nghĩa là (như bạn đã nói) cấu trúc linh hoạt của mảng có thể gây ra sự đau đớn về phát triển sau này

  3. Bạn đang xây dựng một thư viện hoặc cơ sở mã chia sẻ?

    Nếu vậy, sử dụng một lớp học! Hãy suy nghĩ về một số nhà phát triển mới đang sử dụng thư viện của bạn hoặc một nhà phát triển khác ở một nơi khác trong công ty chưa bao giờ sử dụng mã của bạn trước đây. Sẽ dễ dàng hơn nhiều cho họ khi xem xét một định nghĩa lớp và hiểu lớp đó làm gì, hoặc xem một số hàm trong thư viện của bạn chấp nhận các đối tượng của một lớp nhất định, hơn là thử và đoán cấu trúc nào chúng cần tạo trong một số lượng mảng. Ngoài ra, điều này còn liên quan đến các vấn đề về tính nhất quán dữ liệu với # 1: nếu bạn đang phát triển thư viện hoặc cơ sở mã được chia sẻ, hãy tốt với người dùng của bạn: cung cấp cho họ các lớp thực thi tính nhất quán dữ liệu và bảo vệ họ khỏi lỗi khi thiết kế dữ liệu của bạn .

  4. Đây có phải là một phần nhỏ của một ứng dụng hay chỉ là một sự chuyển đổi dữ liệu? Bạn không phù hợp với bất kỳ ở trên?

    Một lớp học có thể là quá nhiều; sử dụng một mảng nếu nó phù hợp với bạn và bạn thấy nó dễ dàng hơn. Như đã đề cập ở trên, nếu bạn chỉ tạo dữ liệu có cấu trúc để gửi dưới dạng JSON hoặc YAML hoặc XML hoặc bất cứ điều gì, đừng bận tâm với một lớp trừ khi có nhu cầu. Nếu bạn đang viết một mô-đun nhỏ trong một ứng dụng lớn hơn và không có mô-đun / nhóm nào khác cần giao diện với mã của bạn, có thể một mảng là đủ.

Cuối cùng, xem xét các nhu cầu mở rộng của mã của bạn và xem xét hơn một mảng có cấu trúc có thể là một sửa chữa nhanh chóng, nhưng một lớp là một giải pháp có khả năng mở rộng và linh hoạt hơn nhiều.

Ngoài ra, hãy xem xét các vấn đề sau: nếu bạn có một lớp và bạn muốn xuất ra JSON, không có lý do gì bạn không thể định nghĩa một json_data()phương thức của lớp trả về một mảng dữ liệu có thể hiểu được JSON trong lớp. Đây là những gì tôi đã làm trong các ứng dụng PHP của mình, nơi tôi cần gửi dữ liệu lớp dưới dạng JSON. Ví dụ:

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

Bạn không thay đổi các yếu tố của my_lineitems, vì vậy việc lấy chúng bằng cách tham chiếu là không cần thiết, đặc biệt vì các biến chỉ giữ một định danh cho đối tượng, không phải chính đối tượng. php.net/manual/en/language.oop5.references.php
Chinoto Vokro

Đã đồng ý. Tôi nghĩ rằng đó là một đoạn của một ứng dụng lớn hơn nhiều cần tham chiếu vì một lý do, hoặc một cái gì đó ...
Josh

2

Bạn có thể thực hiện cấu trúc json bằng giao diện JsonSerializable và sử dụng mảng / lớp lồng nhau theo bất kỳ cách nào. Lớp nhanh hơn trong các hoạt động get / set và dễ dàng gỡ lỗi hơn. Với Array bạn không cần khai bá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'
}
*/

Câu hỏi này thêm gì mà câu trả lời được chấp nhận hiện tại?
esoterik ngày

@esoterik làm tổ. json_encode(['order'=>$o]);trong tồn tại câu trả lời được chấp nhận là trống : {"order":{}}. Chắc chắn bạn có thể sử dụng: $o->forJSON()mọi lúc trên mọi đối tượng ở mọi cấp độ, nhưng thiết kế của nó không thực sự tốt. Bởi vì tất cả thời gian bạn cần để viết một cái gì đó như: $items_json = array(); foreach($this->my_lineitems as $item) $items_json[] = $item->forJSON();
El '
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.