Mặc định so với Impl khi triển khai các giao diện trong Java


34

Sau khi đọc nên gói tên là số ít hay số nhiều? Tôi nhận ra rằng tôi chưa bao giờ thấy một cuộc tranh luận đúng đắn nào về một trong những thú cưng của tôi: đặt tên cho việc triển khai các giao diện.

Giả sử rằng bạn có một giao diện Orderđược dự định thực hiện theo nhiều cách khác nhau nhưng chỉ có triển khai ban đầu khi dự án được tạo lần đầu tiên. Bạn có đi DefaultOrderhoặc OrderImplhoặc một số biến thể khác để tránh sự phân đôi giả? Và bạn sẽ làm gì khi có nhiều triển khai hơn?

Và quan trọng nhất ... tại sao?

Câu trả lời:


59

Tên có cơ hội để truyền đạt ý nghĩa. Tại sao bạn lại vứt bỏ cơ hội đó với Impl?

Trước hết, nếu bạn chỉ có một lần thực hiện, hãy bỏ qua giao diện. Nó tạo ra vấn đề đặt tên này và không thêm gì. Thậm chí tệ hơn, nó có thể gây rắc rối với chữ ký phương thức không nhất quán trong API nếu bạn và tất cả các nhà phát triển khác không cẩn thận chỉ luôn sử dụng giao diện.

Do đó, chúng ta có thể giả định rằng mọi giao diện đều có hoặc hai hoặc nhiều triển khai.

  • Nếu bạn chỉ có một cái ngay bây giờ và bạn không biết người kia có thể khác biệt như thế nào, Mặc định là một khởi đầu tốt.

  • Nếu bạn có hai cái ngay bây giờ, hãy đặt tên cho mỗi cái theo mục đích của nó.

    Ví dụ: Gần đây, chúng tôi có một bối cảnh lớp cụ thể (tham chiếu đến cơ sở dữ liệu). Chúng tôi nhận ra rằng chúng tôi cần có khả năng thể hiện một bối cảnh ngoại tuyến, vì vậy tên Bối cảnh được sử dụng cho giao diện mới (để duy trì khả năng tương thích cho các API cũ) và triển khai mới được tạo ra, OfflineContext . Nhưng đoán xem ban đầu được đổi tên thành gì? Đúng vậy, ContextImpl ( yike ).

    Trong trường hợp này, DefaultContext có thể sẽ ổn và mọi người sẽ nhận được nó, nhưng nó không được mô tả như nó có thể. Rốt cuộc, nếu nó không ngoại tuyến , thì nó là gì? Vì vậy, chúng tôi đã đi với: OnlineContext .


Trường hợp đặc biệt: Sử dụng tiền tố "I" trên các giao diện

Một trong những câu trả lời khác được đề xuất sử dụng tiền tố I trên các giao diện. Tốt hơn là, bạn không cần phải làm điều này.

Tuy nhiên, nếu bạn cần cả hai giao diện, để triển khai tùy chỉnh, nhưng bạn cũng có một triển khai cụ thể chính sẽ được sử dụng thường xuyên và tên cơ bản cho nó chỉ quá đơn giản để từ bỏ một giao diện, sau đó bạn có thể xem xét thêm "Tôi" với giao diện (mặc dù, nó hoàn toàn ổn nếu nó vẫn không phù hợp với bạn và nhóm của bạn).

Ví dụ: Nhiều đối tượng có thể là "EventDispatcher". Vì lợi ích của API, điều này phải phù hợp với một giao diện. Nhưng, bạn cũng muốn cung cấp một bộ điều phối sự kiện cơ bản cho đoàn. DefaultEventDispatcher sẽ ổn, nhưng nó hơi dài và nếu bạn sẽ thấy tên của nó thường xuyên, bạn có thể thích sử dụng tên cơ sở EventDispatcher cho lớp cụ thể và triển khai IEventDispatcher để triển khai tùy chỉnh:

/* Option 1, traditional verbose naming: */
interface EventDispatcher { /* interface for all event dispatchers */ }
class DefaultEventDispatcher implements EventDispatcher {
  /* default event dispatcher */
}

/* Option 2, "I" abbreviation because "EventDispatcher" will be a common default: */
interface IEventDispatcher { /* interface for all event dispatchers */ }
class EventDispatcher implements IEventDispatcher {
  /* default event dispatcher. */
}

3
+1 cho câu trả lời chắc chắn. Giống như lý do đằng sau không sử dụng Impl.
Gary Rowe

2
+1 hoàn toàn đồng ý. Đặt tên một giao diện ra khỏi khái niệm miền mà nó đại diện là hoàn toàn thiếu điểm.
rupjones

9
"Nếu bạn sẽ chỉ có một lần thực hiện", làm sao bạn biết trước rằng bạn sẽ chỉ có một lần thực hiện? "mọi giao diện đều có hoặc có hai hoặc nhiều triển khai" ... trước khi có hai triển khai, trước tiên bạn không có, bạn có một, sau đó bạn có hai, ý tôi là có thể có một thời điểm khi bạn chỉ có một triển khai, chỉ trước khi thực hiện cái thứ hai.
Tulains Córdova

2
@NickC Tôi không phải là chuyên gia về ngữ nghĩa (Tôi là một nhà thơ và tôi thậm chí không biết điều đó). Tiếng Anh không phải là tiếng mẹ đẻ của tôi vì vậy tôi không thể nói về ngôn ngữ này. Tôi đã nói về logic thiếu sót. Bạn sử dụng các giao diện để tách rời. Điều đó không đòi hỏi một số lượng thực hiện nhất định.
Tulains Córdova

4
if you will only ever have one implementation, do away with the interface- trừ khi bạn muốn kiểm tra thành phần trong trường hợp nào bạn có thể muốn giữ giao diện đó để tạo MockOrder, OrderStub hoặc tương tự.
JBRWilkinson

15

Tôi quyết định đặt tên theo trường hợp sử dụng của giao diện.

Nếu giao diện được sử dụng để tách rời , thì tôi chọn Impltriển khai.

Nếu mục đích của giao diện là trừu tượng hóa hành vi , thì việc triển khai được đặt tên theo những gì họ đang thực hiện một cách cụ thể. Tôi thường gắn tên giao diện vào đó. Vì vậy, nếu giao diện được gọi Validator, tôi sử dụng FooValidator.

Tôi thấy đó Defaultlà một lựa chọn rất tồi. Đầu tiên, nó làm ô nhiễm các tính năng hoàn thành mã, bởi vì các tên luôn bắt đầu với nó. Một điều khác là một mặc định có thể thay đổi theo thời gian. Vì vậy, những gì đầu tiên có thể là một mặc định có thể một thời gian sau đó là một tính năng không dùng nữa. Vì vậy, hoặc bạn luôn bắt đầu đổi tên các lớp của mình ngay sau khi mặc định thay đổi hoặc bạn sống với các tên gây hiểu lầm.


8

Tôi đồng ý với câu trả lời của Nicole (đặc biệt là giao diện có thể không cần thiết trong hầu hết các trường hợp), nhưng để thảo luận, tôi sẽ đưa ra một giải pháp thay thế khác ngoài OrderImplDefaultOrder: ẩn việc triển khai đằng sau một phương thức nhà máy tĩnh như thế nào Orders.create(). Ví dụ:

public final class Orders {
  public static Order create() {
    return new Order() {
      // Implementation goes here.
    };
  }
}

Với cách tiếp cận này, việc triển khai có thể là một lớp bên trong ẩn danh hoặc nó có thể là một lớp riêng có Defaulthoặc Impltrong tên hoặc nó có thể được đặt tên hoàn toàn khác. Cho dù lựa chọn nào được thực hiện, người gọi không cần quan tâm, vì vậy bạn sẽ linh hoạt hơn bây giờ và sau này khi / nếu bạn quyết định thay đổi nó.

Một số ví dụ tuyệt vời của mẫu này trong thực tế là các lớp java.util.Collectionsjava.util.concurrent.Executorstiện ích, có các phương thức trả về các triển khai ẩn. Như Java đề cập hiệu quả (trong Mục 1), mẫu này có thể giúp giữ "trọng lượng khái niệm" của API nhỏ hơn.


+1 cho một trường hợp bổ sung thú vị: triển khai ẩn danh.
Gary Rowe

1
Cũng được sử dụng rộng rãi trong ổi .
Nicole

3

Tôi luôn đi OrderImplđơn giản vì nó hiển thị theo thứ tự abc ngay sau Ordergiao diện.


2
Và làm thế nào để bạn xử lý các triển khai tiếp theo đang diễn ra, để xử lý đặc biệt và như vậy?
Gary Rowe

1
Bạn hỏi về việc thực hiện ban đầu. Khi bạn bắt đầu triển khai InternalOrder, ForeignOrder hoặc bất cứ điều gì, chúng được đặt tên chu đáo hơn và không liên quan đến vị trí của chúng trong bảng chữ cái.
Ben Hoffstein

Hoàn toàn đúng - Tôi đã chỉnh sửa câu hỏi ban đầu để phản ánh yêu cầu mới này.
Gary Rowe

Không phải là một ý tưởng tốt Nếu có nhiều hơn một thực hiện.
Sadegh

0

Bạn có thể đặt tên giao diện với tiền tố I (IWhthing) và sau đó thực hiện Any.

Các quy ước mã Java chính thức không nói về kiểu đặt tên này cho các giao diện, tuy nhiên kiểu đặt tên này giúp nhận biết và điều hướng.


Tại sao bạn lại thêm tiền tố vào giao diện với I? Là nó để hỗ trợ công nhận trong điều hướng?
Gary Rowe

@Gary: các loại giao diện tiền tố với I là một quy ước được thiết lập tốt trong ngôn ngữ Delphi. Trong các loại lớp Delphi có tiền tố là T. Vì vậy, chúng ta sẽ có giao diện IOrder và triển khai mặc định TOrder với TS SomethingOrder và TBullMarketOrder như các triển khai cụ thể.
Marjan Venema

3
Tôi đã thấy quy ước này được sử dụng rất nhiều trong phát triển .NET. Có lẽ bởi vì tài liệu và ví dụ của Microsoft sử dụng nó.
Ben Hoffstein

5
Có vẻ như @Renesis đã đưa ra một trường hợp hữu ích để sử dụng nó trong Java. Cá nhân, tôi dựa vào IDE để cho tôi biết sự khác biệt nếu không chúng tôi có E cho Enums, C dành cho các lớp và tất cả đều dư thừa.
Gary Rowe

0

Tôi nghĩ rằng mặc dù Mặc định có thể có ý nghĩa trong một số trường hợp, nhưng sẽ hữu ích hơn khi mô tả việc thực hiện. Vì vậy, nếu giao diện của bạn là UserProfileDAOsau đó triển khai của bạn có thể UserProfileSQLDAOhay UserProfileLDAPDAOhoặc một cái gì đó như thế.


0

nếu có thể, hãy đặt tên cho nó theo cái gì / làm thế nào nó làm điều đó.

Cách tiếp cận tồi tệ nhất là đặt tên cho nó sau khi nó được sử dụng như thế nào.

Nếu nó được coi là được sử dụng làm lớp cơ sở để thực hiện, bạn có thể sử dụng BaseX hoặc AbstractX (nếu nó là trừu tượng (nhưng hãy cố gắng thực hiện những gì nó làm, bởi vì nếu nó không làm gì thì bạn sẽ không tạo ra nó, bạn đã có giao diện). Nếu nó cung cấp chức năng đơn giản nhất có thể và dự kiến ​​sẽ được sử dụng trực tiếp (không mở rộng nó) khi chức năng đó đủ, bạn có thể gọi nó là SimpleX hoặc BasicX.

Nếu nó được sử dụng trừ khi một số triển khai khác được cung cấp, hãy đặt tên là DefaultX


-2

Hầu hết các câu trả lời này mô tả những gì đang được thực hiện nhưng không phải tại sao.

Điều gì đã xảy ra với các nguyên tắc OO? Impl nói gì với tôi về một lớp học và cách sử dụng nó? Bạn nên quan tâm đến việc hiểu các tập quán chứ không phải cách nó được xây dựng.

Nếu tôi tạo giao diện Person, PersonImpl là gì? Vô nghĩa. Ít nhất DefaultPerson cho tôi biết bất cứ ai đã mã hóa nó không nên tạo giao diện.

Các giao diện đang gõ cho đa hình và nên được sử dụng như vậy.


1
Câu trả lời được chấp nhận từ @NickC đi vào một số chi tiết về những gì đang được thực hiện và tại sao.
Gary Rowe
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.