Có một câu hỏi nào chỉ yêu cầu những gì bạn cần về nguyên tắc giao diện không?


9

Tôi đã phát triển việc sử dụng một nguyên tắc để thiết kế và tiêu thụ các giao diện có nội dung cơ bản, "chỉ yêu cầu những gì bạn cần."

Ví dụ: nếu tôi có một loạt các loại có thể bị xóa, tôi sẽ tạo một Deletablegiao diện:

interface Deletable {
   void delete();
}

Sau đó tôi có thể viết một lớp chung:

class Deleter<T extends Deletable> {
   void delete(T t) {
      t.delete();
   }
}

Ở những nơi khác trong mã tôi sẽ luôn yêu cầu trách nhiệm nhỏ nhất có thể để đáp ứng nhu cầu của mã khách hàng. Vì vậy, nếu tôi chỉ cần xóa a File, tôi vẫn sẽ yêu cầu a Deletablechứ không phải a File.

Là nguyên tắc này là kiến ​​thức phổ biến và đã có một tên được chấp nhận? Có gây tranh cãi không? Nó được thảo luận trong sách giáo khoa?


1
Khớp nối lỏng lẻo có thể? Hay giao diện hẹp?
tdammers

Câu trả lời:


16

Tôi tin rằng điều này đề cập đến cái mà Robert Martin gọi là Nguyên tắc phân chia giao diện . Các giao diện được phân tách thành các giao diện nhỏ và súc tích để người tiêu dùng (khách hàng) sẽ chỉ phải biết về các phương pháp mà họ quan tâm. Bạn có thể kiểm tra thêm về RẮN .


4

Để mở rộng câu trả lời rất hay của Vadim, tôi sẽ trả lời câu hỏi "nó có gây tranh cãi không" với "không, không thực sự".

Nói chung, sự phân biệt giao diện là một điều tốt, bằng cách giảm tổng số "lý do để thay đổi" của các đối tượng khác nhau có liên quan. Nguyên tắc cốt lõi là, khi một giao diện có nhiều phương thức phải được thay đổi, giả sử thêm một tham số vào một trong các phương thức giao diện, thì tất cả người tiêu dùng của giao diện ít nhất phải được biên dịch lại, ngay cả khi họ không sử dụng phương thức đã thay đổi. "Nhưng nó chỉ là một biên dịch lại!", Tôi nghe bạn nói; điều đó có thể đúng, nhưng hãy nhớ rằng thông thường, mọi thứ bạn biên dịch lại phải được đẩy ra như một phần của bản vá phần mềm, bất kể sự thay đổi đáng kể đến nhị phân như thế nào. Các quy tắc này ban đầu được khái niệm hóa từ đầu những năm 90, khi máy trạm để bàn trung bình không mạnh hơn điện thoại trong túi của bạn, quay số 14,4k baud là blazin ', và "đĩa mềm" 3,5 "1,44 MB là phương tiện di động chính. Ngay cả trong thời đại 3G / 4G hiện nay, người dùng internet không dây thường có các gói dữ liệu có giới hạn, vì vậy khi phát hành bản nâng cấp, càng ít tệp nhị phân phải được tải xuống thì càng tốt.

Tuy nhiên, giống như tất cả các ý tưởng tốt, sự phân biệt giao diện có thể trở nên tồi tệ nếu được thực hiện không đúng cách. Trước hết, có khả năng bằng cách tách biệt các giao diện trong khi giữ cho đối tượng thực hiện các giao diện đó (hoàn thành các phụ thuộc) tương đối không thay đổi, bạn có thể kết thúc bằng một "Hydra", họ hàng của mô hình chống đối "Thần đối tượng" trong đó tất cả hiểu biết, toàn năng của đối tượng bị ẩn khỏi sự phụ thuộc bởi các giao diện hẹp. Bạn kết thúc với một con quái vật nhiều đầu, ít nhất là khó duy trì như Thần Vật, cộng với chi phí duy trì tất cả các giao diện của nó. Không có số lượng giao diện cứng mà bạn không nên vượt quá, nhưng mỗi giao diện bạn triển khai trên một đối tượng nên được mở đầu bằng cách trả lời câu hỏi, "Giao diện này có đóng góp cho đối tượng không '

Thứ hai, một giao diện cho mỗi phương thức có thể không cần thiết, mặc dù SRP có thể cho bạn biết điều gì. Bạn có thể kết thúc với "mã ravioli"; rất nhiều khối kích cỡ cắn đến mức khó có thể theo dõi để tìm ra chính xác nơi mà mọi thứ thực sự xảy ra. Cũng không cần thiết phải phân chia một giao diện với hai phương thức nếu tất cả người dùng hiện tại của giao diện đó cần cả hai phương thức. Ngay cả khi một trong các lớp phụ thuộc chỉ cần một trong hai phương thức, thì thường không chấp nhận phân chia giao diện nếu các phương thức của nó về mặt khái niệm có độ gắn kết rất cao (ví dụ tốt là "phương pháp phản nghĩa" đối lập chính xác với nhau).

Sự phân biệt giao diện nên dựa trên các lớp phụ thuộc vào giao diện:

  • Nếu chỉ có một lớp phụ thuộc vào giao diện, đừng tách riêng. Nếu lớp không sử dụng một hoặc nhiều phương thức giao diện và đó là người tiêu dùng duy nhất của giao diện, thì bạn không nên tiếp xúc với các phương thức đó ngay từ đầu.

  • Nếu có nhiều hơn một lớp phụ thuộc vào giao diện và tất cả những người phụ thuộc sử dụng tất cả các phương thức của giao diện, đừng tách biệt; nếu bạn phải thay đổi giao diện (để thêm phương thức hoặc thay đổi chữ ký), tất cả người tiêu dùng hiện tại sẽ bị ảnh hưởng bởi thay đổi cho dù bạn có tách riêng hay không (mặc dù nếu bạn thêm phương thức mà ít nhất một người phụ thuộc sẽ không cần, hãy xem xét cẩn thận nếu thay đổi nên được thực hiện như một giao diện mới, có thể kế thừa từ giao diện hiện có).

  • Nếu có nhiều hơn một lớp phụ thuộc vào giao diện và chúng không sử dụng tất cả các phương thức giống nhau, thì đó là một ứng cử viên để phân tách. Nhìn vào "sự gắn kết" của giao diện; làm tất cả các phương pháp hơn nữa một mục tiêu lập trình duy nhất, rất cụ thể? Nếu bạn có thể xác định nhiều mục đích cốt lõi cho giao diện (và người triển khai), hãy xem xét việc chia các giao diện dọc theo các dòng đó để tạo giao diện nhỏ hơn với ít "lý do thay đổi" hơn.


Cũng đáng lưu ý rằng sự phân biệt giao diện có thể tốt và bảnh bao nếu một người sử dụng ngôn ngữ / hệ thống OOP có thể cho phép mã xác định một sự kết hợp chính xác của các giao diện, nhưng ít nhất trong .NET họ có thể gây ra một số vấn đề nghiêm trọng, vì không có vấn đề gì cách chỉ định một bộ sưu tập "những thứ thực hiện IFoo và IBar, nhưng có thể không có gì chung".
supercat

Các tham số loại chung có thể được xác định với các tiêu chí bao gồm triển khai nhiều giao diện, nhưng bạn đúng trong các biểu thức yêu cầu loại tĩnh thường không thể hỗ trợ chỉ định nhiều giao diện. Nếu có nhu cầu loại tĩnh thực hiện cả IFoo và IBar và bạn điều khiển cả hai giao diện đó, thì có thể nên thực hiện IBaz : IFoo, IBarvà yêu cầu thay thế.
KeithS

Nếu mã máy khách có thể cần một cái gì đó có thể được sử dụng IFooIBar, việc xác định hỗn hợp IFooBarcó thể là một ý tưởng tốt, nhưng nếu các giao diện được phân chia tốt, cuối cùng sẽ dễ dàng yêu cầu hàng tá loại giao diện riêng biệt. Xem xét các bộ sưu tập tính năng sau đây có thể có: Liệt kê, đếm báo cáo, phần tử đọc thứ n, phần tử thứ n, Chèn trước phần tử thứ n, phần tử thứ n, mục mới (thu phóng và chỉ mục trả về của không gian mới) và Thêm. Chín phương pháp: ECRWIDNA. Tôi có thể có thể mô tả hàng chục loại mà tự nhiên sẽ hỗ trợ nhiều kết hợp khác nhau.
supercat

Mảng, ví dụ, sẽ hỗ trợ ECRW. Một danh sách mảng sẽ hỗ trợ ECRWIDNA. Danh sách an toàn luồng có thể hỗ trợ ECRWNA [mặc dù A thường chỉ hữu ích cho việc điền trước danh sách]. Một trình bao bọc mảng chỉ đọc có thể hỗ trợ ECR. Một giao diện danh sách covariant có thể hỗ trợ ECRD. Giao diện không chung có thể cung cấp C hoặc CD hỗ trợ an toàn loại. Nếu Hoán đổi là một tùy chọn, một số loại có thể hỗ trợ CS nhưng không phải D (ví dụ: mảng) trong khi các loại khác sẽ hỗ trợ CDS. Cố gắng xác định các loại giao diện riêng biệt cho từng tổ hợp khả năng cần thiết sẽ là một cơn ác mộng.
supercat

Bây giờ hãy tưởng tượng người ta muốn có khả năng bọc một bộ sưu tập với một đối tượng có thể làm mọi thứ mà bộ sưu tập có thể làm, nhưng nó ghi lại mọi giao dịch. Cần bao nhiêu cái bao bọc? Nếu tất cả các bộ sưu tập được kế thừa từ một giao diện chung bao gồm các thuộc tính để xác định khả năng của chúng, một trình bao bọc sẽ đủ. Nếu tất cả các giao diện là khác nhau, tuy nhiên, người ta sẽ cần hàng tá.
supercat
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.