Mỗi lớp tôi có nên tuân thủ một giao diện không?


10

Tôi đang viết một trò chơi trong Bản mô tả và quyết định rằng tôi sẽ cố gắng tuân thủ ý tưởng " lập trình dựa trên giao diện ", trong đó bạn viết mã dựa trên giao diện, thay vì thực hiện, của một đối tượng.

Tôi đã viết ra một số lượng lớn các giao diện và các lớp triển khai chúng, sau đó lùi lại một bước và nhận ra rằng các lớp đó đủ đơn giản mà tôi sẽ không bao giờ cần phải thay đổi việc thực hiện, vì thực sự chỉ có một cách để làm những gì lớp nào (di chuyển một Phaser.Spritecách hạn chế để hành động như một chiếc xe tăng).

Sau đó, tôi nhớ đã đọc một vài năm trước về ý tưởng về YAGNI , về cơ bản là bạn không nên thiết kế quá mức mã của mình để bao gồm những thứ bạn không bao giờ có thể sử dụng.

Theo các thực tiễn tốt nhất, mỗi lớp nên thực hiện một giao diện, hay bạn nên giới hạn nó ở các lớp mà bạn dự kiến ​​sẽ có khả năng hoán đổi trong tương lai?


Hãy nhớ rằng, khi bạn sử dụng một lớp thực tế làm tham số, bạn cũng đang mã hóa một giao diện, đến giao diện của lớp này. Không ai bắt buộc bạn sử dụng lớp cụ thể đó, bạn cũng có thể kế thừa nó và cung cấp chức năng hoàn toàn mới, nhưng điều đó có vẻ không đúng. Giao diện tồn tại để làm cho mọi người quấn đầu xung quanh ý tưởng dễ dàng hơn. Với điều đó thật đáng buồn, không, bạn không thực sự cần giao diện cho mọi thứ.
Andy

1
Bản sao có thể có của Nhu cầu thêm giao diện cho mọi lớp
gnat

Câu trả lời:


6

Lý do để có giao diện là vì nó đơn giản hóa đa hình. Có nghĩa là bạn có thể gửi các trường hợp bằng hợp đồng thay vì biết thực hiện thực tế của họ. Chẳng hạn, bạn có thể gửi "Trình đọc" tới một phương thức, để phương thức được gọi như vậy có thể sử dụng phương thức "read ()". Bằng cách khai báo giao diện "Reader", bạn có thể làm cho bất kỳ đối tượng nào tuân thủ hợp đồng này, bằng cách thực hiện các phương thức mà nó chỉ định. Bằng cách này, bất kỳ người gọi nào cũng có thể cho rằng các phương thức nhất định sẽ tồn tại, ngay cả khi các đối tượng bên dưới hợp đồng có thể hoàn toàn khác nhau.

Điều này tách rời tính đa hình từ một lớp cơ sở, và làm cho nó có thể giữa các biên giới chuỗi lớp, bằng cách ngụ ý một hợp đồng.

Bây giờ ... Điều này chỉ hữu ích nếu bạn có nhu cầu cho nhiều chuỗi thừa kế hoạt động theo cùng một cách hoặc nếu bạn sẽ có nhiều lớp cơ sở với hành vi phổ biến mà bạn sẽ muốn truyền cho người dùng khác.

Nếu bạn chỉ có MỘT chuỗi thừa kế (lớp cơ sở-> lớp con) hoặc chỉ cơ sở hoặc bạn không cần phải thực sự PASS các đối tượng liên quan cho người khác hoặc sử dụng chúng một cách chung chung, thì bạn sẽ không thêm triển khai giao diện, vì đó là khái niệm vô ích.


Tôi cũng nói thêm rằng các giao diện nên mô hình hóa trừu tượng, vì vậy đừng tạo chúng bằng cách đơn giản là nâng tất cả các thành viên công khai của một lớp. Hãy suy nghĩ về những gì lớp đó đại diện, và chỉ đặt những thứ đó trong giao diện mà bất kỳ đối tượng nào thuộc loại đó nên có. Bạn cũng có thể kết thúc việc tạo nhiều giao diện (ví dụ Movesvà ví dụ Shootsvề xe tăng của bạn) cho một lớp.
TMN

7

Nếu bạn không chắc chắn nếu bạn thực sự cần các giao diện, thì có lẽ bạn không. Đây là cốt lõi của nguyên tắc YAGNI. Khi bạn cần có thể hoán đổi việc thực hiện, sau đó bạn giới thiệu một giao diện.


6

Tạo một giao diện cho mỗi lớp là một chút quá mức cần thiết. Từ quan điểm hoàn toàn hướng đối tượng, mỗi lớp đã có một giao diện . Một giao diện không có gì khác hơn các phương thức và thành viên dữ liệu của lớp.

Chúng tôi thường sử dụng "giao diện" để có nghĩa là "một lớp Java được xác định bằng cách sử dụng interfacetừ khóa" hoặc "một lớp C ++ chỉ có các hàm ảo công khai, thuần túy". Đây là những tóm tắt hữu ích, bởi vì chúng tách rời giao diện của một lớp khỏi việc thực hiện nó.

Với ý nghĩ đó, sử dụng giao diện khi thích hợp.

  • Sẽ có nhiều triển khai cho một giao diện? Nếu vậy, tình huống này có thể là một ứng cử viên để trích xuất các phương thức phổ biến, công khai vào một giao diện.

  • Tôi sẽ chuyển giao việc triển khai giao diện tiềm năng cho các mô-đun khác không biết hoạt động bên trong mô-đun của mình chứ? Một giao diện có thể hữu ích ở đây, vì nó làm giảm kích thước của điểm chạm giữa các mô-đun.

Lưu ý các từ chồn ở trên: "có thể" là một ứng cử viên, "có thể" hữu ích. Không có quy tắc cứng và nhanh nào nói "có" hoặc "không" cho một tình huống nhất định.

Điều đó đang được nói, có một giao diện cho tất cả mọi thứ rất có thể là quá mức cần thiết. Nếu bạn thấy mô hình này, bạn sẽ đi xa:

interface I {
  int getA()
  int getB()
  ...
  int getY()
  int getZ()
}

class C : I {
  int getA() { ... }
  int getB() { ... }
  ...
  int getY() { ... }
  int getZ() { ... }
}

Một lý do nữa cho các giao diện là nếu hệ thống kiểm tra của bạn làm cho nó cần thiết, điều này có thể phụ thuộc vào ngôn ngữ và khung kiểm tra của bạn. Hy vọng rằng bạn không cần phải làm điều này, tất nhiên.
Darien

@Darien: Lý do hệ thống kiểm tra có thể yêu cầu giao diện là vì thử nghiệm thực sự đang sử dụng một triển khai (giả định) khác, đưa bạn trở lại viên đạn đầu tiên khi giao diện phù hợp.
Bart van Ingen Schenau

Một số thứ tuyệt vời ở đây. Chỉ cần cân nhắc điều này: Một giao diện không gì khác hơn là các phương thức và thành viên dữ liệu đối mặt với công chúng của một lớp . Mặc dù điều này đúng với việc triển khai giao diện , nhưng nó chắc chắn không đúng với giao diện không bao giờ được thực hiện, mặc dù được cấp - đây sẽ là một thứ gì đó có mùi mã.
Robbie Dee

@RobbieDee đúng như vậy, ví dụ như Java interface, thật ra mọi thứ trong Java interfacecũng là giao diện công khai của nó (khái niệm OO).

Tất cả mọi người! Viết câu trả lời thứ 2 & 3 của câu trả lời. Cán mỏng nó. Đặt nó trong ví của bạn. Tham khảo thường xuyên.
radarbob

5

Các nhà phát triển mới hơn có thể nghĩ rằng các giao diện là một sự bất tiện mà bạn chỉ cần thêm vào vì bạn nói đó là "cách thực hành tốt nhất". Nếu bạn đang mù quáng làm điều này, nó smacks của lập trình hàng hóa sùng bái .

Có một số lý do rất chính đáng mà bạn nên xem xét các giao diện cho các phát triển lớn hơn thay vì tập hợp một nhóm các lớp.

Khung mô phỏng

Nếu bạn muốn thử nghiệm một ứng dụng nhiều lớp mà không phải tạo các đối tượng cho các lớp khác nhau, chắc chắn bạn sẽ muốn sử dụng các khung mô phỏng để chế nhạo các lớp. Giao diện được sử dụng rộng rãi ở đây.

Phụ thuộc tiêm

Mặc dù chúng không được ưa chuộng phần nào do sự phình to mà chúng thêm vào, ý tưởng là âm thanh - khả năng trao đổi đơn giản trong các bê tông dựa trên giao diện. Nguyên tắc cũng có thể được tìm thấy trong nhiều mẫu thiết kế như mẫu nhà máy.


Các phương pháp này được tóm tắt trong D từ các nguyên tắc RẮN . Chà của điều này là - sử dụng trừu tượng, không cụ thể hóa.

Giống như các mẫu thiết kế, khi sử dụng chúng là một cuộc gọi phán xét. Điểm quan trọng là để hiểu làm thế nào các giao diện hữu ích không chỉ gắn chúng trên các lớp của bạn bởi vì bạn cảm thấy bạn phải làm. Có một cuộc thảo luận tốt về các giá trị khác nhau của DIP và YAGNI ở đây .


Lưu ý rằng tiêm phụ thuộc và các thùng chứa IoC là hai thứ hoàn toàn khác nhau (một là nguyên tắc thiết kế và thứ hai là một cách cụ thể hóa nguyên tắc đó vào thực tế bằng cách sử dụng các khung khác nhau). Bạn có thể sử dụng DI mà không cần sử dụng bộ chứa IoC.
sara

@kai "Bạn có thể sử dụng DI mà không cần sử dụng bộ chứa IoC" . Tôi nghĩ rằng các cửa hàng phát triển trưởng thành hơn có thể (và làm). Đơn giản là không cần phải làm ô nhiễm mã cho những gì rõ ràng là một khái niệm đơn giản. Joel là một quan điểm tương tự.
Robbie Dee

YAGNI rất khó khăn. Dù triết lý là gì, trong hầu hết các trường hợp, trong thực tế, YAGNI được sử dụng như một cái cớ để cắt góc. Tránh khớp nối nên được thực hiện nghiêm túc hơn. Thật là bực bội khi thấy trong nhiều cuộc họp scrum, một nhà phát triển tự gọi mình bị chặn vì người khác không hoàn thành công việc của họ. Làm thế nào để chặn một nhà phát triển khác đang tiết kiệm thời gian của bất cứ ai? Đề nghị của tôi là sử dụng một giao diện để tránh khớp nối. Tất nhiên, người ta không phải sử dụng giao diện cho các lớp Model.
Ripal Barot
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.