Người dịch: Trần Thanh Dân
Tôi ghét phải xử lý mảng kết hợp khi đang viết code cho client. Vấn đề với mảng là nó không có ngữ cảnh, không có kiến thức gì đặc biệt. Mảng đơn giản là một phần data được gói gọn theo một format nhất định không được thuận tiện lắm. Tồi tệ nhất là chúng ràng buộc ta vào một cách triển khai nhất định.
Tôi không có ý nói rằng bạn không nên trả về các mảng có dữ liệu thống nhất (return arrays that have uniform data). Việc trả về mảng của một loại đối tượng là tốt, dù cho bạn không dám chắc được là nó sẽ chỉ chứa một số loại data nhất định qua API.
Mảng có thể hoạt động như phần dữ liệu đại diện cho một số lớp (class) nội bộ, và xin nhấn mạnh là nội bộ – là state hợp lí nhất mà nó nên xuất hiện. Cái tôi muốn nói tới là
Xem xét đoạn mã sau:
class AcmeFileSender implements FileSender { public function sendFiles(FileCollection $files): array { $sent = 0; $errors = []; foreach ($files as $file) { try { $this->send($file); $sent++; } catch (SenderException $exception) { $errors[] = $exception->getMessage(); } } return [ "sent" => $sent, "failed" => count($errors), "errors" => $errors ]; } public function send(File $file): void {//...} }
Vấn đề trong trường hợp này là, Chúng ta không thể biết được cấu trúc chính xác của mảng này. Điều này có nghĩa là khi chúng ta viết code ở phía client chúng ta sẽ phụ thuộc vào việc thực hiện phương thức sendFiles()
. Chúng ta phải biết được chính xác khóa (key) là gì và loại dữ liệu (sort of data) của khóa đó.
Trong hướng đối tượng (OOP) chúng ta không nên quan tâm đến chi tiết thực hiện của các lớp khác, chúng ta chỉ cần quan tâm đến các phương thức hiện tại (interfaces method).
Gần đây tôi có bị vấp phải một điều gì đó có thể là một vi phạm nguyên trọng hơn:
public function processStuff($stuff): array { //... return [$someArray, $someObject]; } //... in client code list($someArray, $someObject) = $this->processor->processStuff($stuff);
Bây giờ, nó là một cách để trả tồi tệ (this kind of abominations) từ phương thức riêng tư (private methods). Nhưng khi bạn đang bắt đầu thiết kế API công bố (API public) của bạn thì tất cả các hy vọng sẽ bị mất đi. Điều này có nghĩa là bạn đang xử lý với mã thủ tục (procedural code base). Trong mã thủ tục mọi thứ đều là dữ liệu và có thể sẽ bị chi phối.
Trong trường hợp trên vấn đề là thiếu định nghĩa văn bản (context). Điều này có nghĩa là khi nào bạn nhận một someArray
và một someObject
cùng thời điểm? Tại sao nó trả về cùng thời điểm? Rõ rang việc thực hiện phương thức processStuff()
sẽ biết được về cách mà nó được sử dụng. Vì vậy nó không được thiết kế để dán cùng với một vài mã ở client. Mã client được kết hợp chặt chẽ với việc thực hiện của phương thức processStuff()
.
Trong nhiều trường hợp, có một số nhầm lẫn là cách xác định nhiệm vụ. Mã làm gì đó và trả về dấu hiệu thành công đến client. Không có nhiều sự tương tác giữa server và client.
Chúng ta cần làm gì để cho ví dụ ban đầu tốt hơn? Giá trị các đối tượng. sendFile()
sẽ có nhiều ý nghĩa hơn bằng cách sử dụng giá trị của đối tượng.
interface SenderReport { public function sent(): int; public function failures(): int; public function errors(): Iterator; public function print(): string; }
Chúng ta sẽ cập nhật FileSender và implementations nó.
class AcmeFileSender implements FileSender { //... public function sendFiles(FileCollection $files): SenderReport { //... return new FileSenderReport($sent, count($errors), $errors); } }
Bây giờ chúng ta có thể dựa vào interface của SenderReport
thay vì triển khai FileSender::sendFiles()
. Bây giờ chúng ta đã có định nghĩa văn bản (context). Ngoài ra, chúng ta có thể thực hiện thay đổi việc triển khai (implementations) FileSender
một cách tự do.
—
Tóm lại, làm ơn, đừng return mảng kết hợp từ public API. Hãy đánh giá lại kể cả khi phải return value. Và nếu bắt buộc thì cũng return cái khác có interface mà bạn và user có thể tin tưởng dùng.
TopDev via dev.to