Tại sao xác định một đối tượng Java bằng giao diện (ví dụ: Bản đồ) thay vì triển khai (HashMap)


17

Trong hầu hết các mã Java, tôi thấy mọi người khai báo các đối tượng Java như thế này:

Map<String, String> hashMap = new HashMap<>();
List<String> list = new ArrayList<>();

thay vì:

HashMap<String, String> hashMap = new HashMap<>();
ArrayList<String> list = new ArrayList<>();

Tại sao có một ưu tiên để xác định đối tượng Java bằng cách sử dụng giao diện chứ không phải là việc triển khai thực sự sẽ được sử dụng?


Câu trả lời:


26

Lý do là việc triển khai các giao diện này thường không liên quan khi xử lý chúng, do đó, nếu bạn bắt buộc người gọi phải truyền HashMapmột phương thức, thì về cơ bản bạn bắt buộc phải sử dụng triển khai nào. Vì vậy, theo nguyên tắc chung, bạn phải xử lý giao diện của nó thay vì triển khai thực tế và tránh những đau đớn và đau khổ có thể dẫn đến việc phải thay đổi tất cả chữ ký phương thức bằng cách sử dụng HashMapkhi bạn quyết định sử dụng LinkedHashMapthay thế.

Cần phải nói rằng có những ngoại lệ cho điều này khi thực hiện có liên quan. Nếu bạn cần một bản đồ khi thứ tự là quan trọng, thì bạn có thể yêu cầu một TreeMaphoặc một LinkedHashMapthông qua, hoặc tốt hơn là SortedMapkhông chỉ định thực hiện cụ thể. Điều này bắt buộc người gọi nhất thiết phải vượt qua một loại triển khai Bản đồ nhất định và gợi ý mạnh mẽ rằng thứ tự quan trọng. Điều đó nói rằng, bạn có thể ghi đè SortedMapvà vượt qua một chưa được sắp xếp? Vâng, tất nhiên, tuy nhiên, mong đợi những điều xấu sẽ xảy ra như là kết quả.

Tuy nhiên, thực tiễn tốt nhất vẫn cho rằng nếu nó không quan trọng, bạn không nên sử dụng các triển khai cụ thể. Điều này nói chung là đúng. Nếu bạn đang xử lý DogCatcó nguồn gốc từ đâu Animal, để sử dụng tốt nhất quyền thừa kế, bạn thường nên tránh có các phương pháp cụ thể Doghoặc Cat. Thay vào đó, tất cả các phương thức trong Doghoặc Catnên ghi đè các phương thức vào Animalvà nó sẽ giúp bạn tránh được rắc rối về lâu dài.


Khi bạn cần một bản đồ được sắp xếp, loại tham số nên SortedMap, không phải TreeMap.
Cephalepad

@Arian SortedMaplà một trong một số triển khai liên quan đến việc đặt hàng. Đó là ngoài quan điểm. TreeMapcũng sắp xếp các mục theo cách triển khai của khóa Comparablehoặc được cung cấp Comparatorgiao diện.
Neil

Không, SortedMap không phải là một triển khai, đó chính xác là vấn đề. Đây là giao diện cho các bản đồ sắp xếp theo khóa.
Cephalepad

1
@Arian Ah Tôi hiểu ý của bạn. Đúng, Sắp xếp bản đồ tốt hơn vì điều đó không bắt buộc phải thực hiện. Tôi sẽ thực hiện các điều chỉnh thích hợp.
Neil

Trên thực tế, LinkedHashMapkhông thực hiện SortedMap. Các lớp con duy nhất của SortedMapConcurrentSkipListMapTreeMap.
bcorso

10

Theo cách nói của Layman:

Lý do tương tự các nhà sản xuất apliances điện đã xây dựng các sản phẩm của họ bằng phích cắm điện thay vì tháo dây cáp đơn giản, và các ngôi nhà đi kèm với ổ cắm trên tường thay vì bóc cáp ra khỏi tường.

Thay vào đó, bằng cách sử dụng phích cắm tiêu chuẩn, họ cho phép cắm cùng các ứng dụng trong bất kỳ phích cắm tương thích nào quanh nhà.

Từ quan điểm của ổ cắm trên tường, việc bạn cắm TV hay âm thanh nổi không thành vấn đề.

Điều đó làm cho cả thiết bị và ổ cắm hữu ích hơn.

Lấy ví dụ một phương thức chấp nhận Bản đồ làm đối số.

Phương thức này sẽ hoạt động bất kể bạn truyền HashMap hay LinkedHashMap cho nó, miễn là nó là một lớp con của Map.

Đó là nguyên tắc thay thế Liskov .

Trong mã mẫu bạn đã cung cấp, điều đó có nghĩa là sau này, bạn có thể thay đổi cách triển khai cụ thể của Hash và bạn sẽ không cần thay đổi phần còn lại của mã.

Vấn đề với phần mềm là, vì việc thay đổi mọi thứ sau này tương đối dễ dàng mà không lãng phí gạch hay vữa, mọi người cho rằng kiểu suy nghĩ trước đó không đáng giá. Nhưng thực tế đã cho chúng ta thấy rằng bảo trì phần mềm rất tốn kém.


4

Đó là tuân theo nguyên tắc phân tách giao diện ('Tôi' trong RẮN ). Nó ngăn chặn mã sử dụng các đối tượng đó tùy thuộc vào phương thức của các đối tượng mà nó không cần, điều này làm cho mã ít được ghép nối hơn và do đó dễ thay đổi hơn.

Ví dụ: nếu bạn phát hiện ra sau này bạn thực sự cần một LinkedHashMap, bạn có thể thực hiện thay đổi đó một cách an toàn mà không ảnh hưởng đến bất kỳ mã nào khác.

Tuy nhiên, có một sự đánh đổi, bởi vì bạn giới hạn một cách giả tạo mã có thể lấy đối tượng của bạn làm tham số. Nói rằng có một chức năng ở đâu đó đòi hỏi một HashMaplý do. Nếu bạn trả về a Map, bạn không thể chuyển đối tượng của mình vào hàm đó. Bạn phải cân bằng khả năng đôi khi trong tương lai cần các chức năng bổ sung trong lớp cụ thể hơn với mong muốn hạn chế khớp nối và giữ giao diện công cộng của bạn nhỏ nhất có thể.


3

Việc biến bị ràng buộc với một giao diện đảm bảo không có cách sử dụng nào của biến đó sẽ sử dụng HashMapchức năng cụ thể có thể không tồn tại trên giao diện, do đó, trường hợp có thể được thay đổi mà không cần quan tâm đến việc triển khai khác, miễn là trường hợp mới cũng thực hiện giao diện.

Vì lý do này, bất cứ khi nào bạn muốn sử dụng giao diện đối tượng, việc khai báo các biến của bạn là giao diện và không phải là triển khai cụ thể, điều này phù hợp với tất cả các loại đối tượng bạn có thể sử dụng có giao diện. Lý do bạn thấy nó thường là nhiều người đã xây dựng điều này như một thói quen.

Điều đó nói rằng, nó không gây hại cho skip ra của việc sử dụng giao diện đôi khi, và hầu hết chúng ta cẩu thả không luôn luôn tuân theo quy tắc này, không có thiệt hại thực sự. Đó chỉ là một thực tiễn tốt để tuân thủ khi bạn có bất kỳ cảm giác nào về mã có thể được thay đổi và cần bảo trì / tăng trưởng trong tương lai. Sẽ không có gì đáng lo ngại khi bạn hack mã mà bạn không nghi ngờ sẽ có tuổi thọ cao hoặc có nhiều tầm quan trọng. Ngoài ra, việc phá vỡ quy tắc này thường có một hậu quả nhỏ là việc thay đổi việc thực hiện sang một quy tắc khác có thể cần một chút tái cấu trúc, vì vậy nếu bạn không luôn tuân theo nó, bạn sẽ không tự làm tổn thương mình nhiều, mặc dù theo đó cũng không có hại gì. .

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.