Có lý do chính đáng để sử dụng giao diện Bộ sưu tập của Java không?


11

Tôi đã nghe lập luận rằng bạn nên sử dụng giao diện chung nhất có sẵn để bạn không bị ràng buộc với việc triển khai cụ thể giao diện đó. Liệu logic này có áp dụng cho các giao diện như java.util.Collection không?

Tôi rất muốn nhìn thấy một cái gì đó như sau:

List<Foo> getFoos()

hoặc là

Set<Foo> getFoos()

thay vì

Collection<Foo> getFoos()

Trong trường hợp cuối cùng, tôi không biết tôi đang xử lý loại dữ liệu nào, trong khi trong hai trường hợp đầu tiên tôi có thể đưa ra một số giả định về thứ tự và tính duy nhất. Liệu java.util.Collection có một bên ngoài hữu ích của việc cha mẹ hợp lý cho cả hai bộ và danh sách?

Nếu bạn gặp phải mã sử dụng Bộ sưu tập khi thực hiện đánh giá mã, làm thế nào bạn xác định liệu việc sử dụng nó có hợp lý hay không và bạn có đề xuất gì để thay thế nó bằng giao diện cụ thể hơn?


3
Bạn có tình cờ nhận thấy trong java.security.cert rằng một kiểu trả về là Collection<List<?>>không? Nói về kinh dị mã hóa!
Macneil

1
@Macneil Tôi không biết bạn muốn nói đến lớp nào, nhưng kiểu trả về như vậy thực sự có thể khá hợp lý. Về cơ bản, nó cho bạn biết rằng bạn có một bộ sưu tập (nghĩa là chứa một loạt những thứ không có thứ tự hợp lý) của danh sách (tức là chứa những thứ thứ tự hợp lý) của các đối tượng (tức là các mặt hàng mà chúng ta không biết tĩnh dù lý do là gì). Có vẻ không hợp lý với tôi.
Zero3

Câu trả lời:


13

Trừu tượng sống lâu hơn so với thực hiện

Nói chung, thiết kế của bạn càng trừu tượng thì càng có khả năng duy trì hữu ích. Vì vậy, vì Bộ sưu tập trừu tượng hơn là các giao diện phụ nên thiết kế API dựa trên Bộ sưu tập có nhiều khả năng vẫn hữu ích hơn so với giao diện dựa trên Danh sách.

Tuy nhiên, nguyên tắc bao trùm là sử dụng sự trừu tượng thích hợp nhất . Vì vậy, nếu bộ sưu tập của bạn phải hỗ trợ các phần tử theo thứ tự thì bắt buộc phải có Danh sách, nếu không có bản sao thì bắt buộc phải đặt Bộ, v.v.

Một lưu ý về thiết kế giao diện chung

Vì bạn quan tâm đến việc sử dụng giao diện Bộ sưu tập với thuốc generic, bạn có thể hữu ích sau đây. Java hiệu quả của Joshua Bloch khuyến nghị cách tiếp cận sau đây khi thiết kế giao diện sẽ dựa vào khái quát: Nhà sản xuất mở rộng, Người tiêu dùng siêu

Điều này còn được gọi là quy tắc PECS . Về cơ bản, nếu các bộ sưu tập chung tạo ra dữ liệu được truyền cho lớp của bạn, chữ ký sẽ trông như thế này:

public void pushAll(Collection<? extends E> producerCollection) {}

Do đó, kiểu đầu vào có thể là E hoặc bất kỳ lớp con nào của E (E được định nghĩa là cả siêu lớp và lớp con của chính nó trong ngôn ngữ Java).

Ngược lại, một bộ sưu tập chung được truyền vào để tiêu thụ dữ liệu nên có chữ ký như thế này:

public void popAll(Collection<? super E> consumerCollection) {}

Phương pháp này sẽ xử lý chính xác với bất kỳ siêu lớp nào của E. Nhìn chung, sử dụng phương pháp này sẽ giúp giao diện của bạn ít gây ngạc nhiên hơn cho người dùng của bạn vì bạn sẽ có thể truy cập Collection<Number>Collection<Integer>xử lý chúng một cách chính xác.


6

Các Collectiongiao diện, và các hình thức dễ dãi nhất Collection<?>, là rất tốt cho các thông số mà bạn chấp nhận. Dựa vào việc sử dụng trong thư viện Java, nó phổ biến hơn như một kiểu tham số hơn là kiểu trả về.

Đối với các loại trả về, tôi nghĩ rằng quan điểm của bạn là hợp lệ: Nếu mọi người dự kiến ​​sẽ truy cập vào đó, họ nên biết thứ tự (theo nghĩa Big-O) của hoạt động đang được thực hiện. Tôi sẽ lặp lại một Collectiontrả lại và thêm nó vào một Bộ sưu tập khác, nhưng có vẻ hơi điên khi gọi containsnó, không biết đó là thao tác O (1), O (log n) hay O (n). Tất nhiên, chỉ vì bạn Setkhông có nghĩa là đó là một bộ băm hoặc một bộ đã được sắp xếp, nhưng đến một lúc nào đó, bạn sẽ đưa ra giả định rằng giao diện đã được triển khai hợp lý (và sau đó bạn sẽ cần lên kế hoạch B nếu giả định của bạn được hiển thị là không chính xác).

Như Tom đề cập, đôi khi bạn cần trả lại Collectionđể duy trì đóng gói: Bạn không muốn các chi tiết triển khai bị rò rỉ, ngay cả khi bạn có thể trả lại một cái gì đó cụ thể hơn. Hoặc, trong trường hợp Tom đã đề cập, bạn có thể trả lại container cụ thể hơn, nhưng sau đó bạn phải xây dựng nó.


2
Tôi nghĩ rằng điểm thứ hai là một chút yếu. Bạn không biết bộ sưu tập sẽ hoạt động như thế nào bất kể đó là Bộ sưu tập hay Danh sách - vì chúng chỉ là trừu tượng. Trừ khi bạn có một lớp học cuối cùng cụ thể, bạn thực sự không thể nói.
Đánh dấu H

Ngoài ra, nếu tất cả những gì bạn biết là một cái gì đó là Bộ sưu tập, bạn không biết liệu nó có thể chứa các bản sao hay không. Xoay quanh điều đó, một khi trường hợp trả về Bộ sưu tập có thể phù hợp là nếu bạn có một bộ sưu tập không chứa các bản sao và không có thứ tự đáng kể (mà tự nhiên sẽ là Bộ), nhưng vì lý do chính đáng, việc triển khai phương thức trả về sử dụng Danh sách. Bạn sẽ không muốn trả về một Danh sách, bởi vì điều đó hàm ý thứ tự quan trọng, nhưng bạn không thể trả lại một Bộ mà không vượt qua sự khắt khe của việc tạo một Danh sách. Vì vậy, bạn trả lại một Bộ sưu tập.
Tom Anderson

@Tom: Điểm tốt!
Macneil

5

Tôi sẽ nhìn nó từ quan điểm hoàn toàn trái ngược và hỏi:

Nếu bạn gặp phải mã đã sử dụng Danh sách <> khi thực hiện đánh giá mã, làm thế nào bạn xác định liệu việc sử dụng nó có hợp lý không?

Nó khá dễ dàng để biện minh cho điều này. Bạn sử dụng Danh sách khi bạn cần một số chức năng không được Bộ sưu tập cung cấp. Nếu bạn không cần chức năng bổ sung đó - bạn có lý do gì? (Và tôi sẽ không mua, "Tôi muốn thấy nó")

Có rất nhiều trường hợp bạn sẽ sử dụng một bộ sưu tập cho mục đích chỉ đọc, điền vào tất cả cùng một lúc, lặp đi lặp lại hoàn toàn - bạn có bao giờ cần lập chỉ mục thủ công không?

Để đưa ra một ví dụ thực tế. Nói rằng tôi thực hiện một truy vấn đơn giản trên cơ sở dữ liệu. ( SELECT id,name,rep FROM people WHERE name LIKE '%squared') Tôi lấy lại dữ liệu có liên quan, điền các đối tượng Person và đưa em vào PersonList)

  • Tôi có cần truy cập theo chỉ mục? - Sẽ là vô nghĩa. Không có ánh xạ giữa chỉ mục và ID.
  • Tôi có cần phải chèn vào một chỉ mục? - Không, DBMS sẽ quyết định đặt nó ở đâu, nếu tôi thêm vào.

Vậy tôi sẽ có lý do gì cho những phương pháp bổ sung đó? (dù sao sẽ không được thực hiện trong PersonList của tôi)


Điểm công bằng. Tôi cho rằng câu hỏi của tôi đề cập đến trường hợp cụ thể trong đó, trong khi thực hiện đánh giá mã, tôi vẫn thấy các DAO trả về Bộ sưu tập, nhưng tôi biết rằng các cuộc gọi DAO này sẽ luôn trả về các tập hợp thực thể; sự tranh cãi của tôi là trong những trường hợp này, kiểu trả về biểu thị tính duy nhất và thông tin này hữu ích cho bất kỳ ai phải sử dụng phương pháp đó (ví dụ: tôi không phải kiểm tra trùng lặp).
Fil

Nếu bạn đã truy vấn DB, việc so sánh 2 đối tượng kết quả với bằng () hoàn toàn không tạo ra đúng - vì vậy bạn cần một cách khác để so sánh các đối tượng để sao chép (ví dụ: chúng có cùng tên, cùng ID không, cả hai?). Bạn cần nói với DAO của bạn cách so sánh chúng nếu muốn loại bỏ trùng lặp - nhưng vì bạn là người dùng quyết định liệu có tồn tại trùng lặp hay không - việc thực hiện với bộ sưu tập từ mã cuộc gọi sẽ dễ dàng hơn. (Để tránh nhiều lớp trừu tượng hơn để thông báo cho DAO cách thực hiện mọi kiểm tra công bằng có thể có trên hành tinh.)
Đánh dấu H

Đồng ý, nhưng chúng tôi đang sử dụng Hibernate và đảm bảo rằng các thực thể của chúng tôi triển khai bằng (). Vì vậy, khi một DAO đang trả về các thực thể, chúng ta có thể nhanh chóng thực hiện Hashset (). AddAll (kết quả) mới và đưa nó trở lại phương thức được gọi.
Fil
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.