Khi nào nên sử dụng giao diện (kiểm tra đơn vị, IoC?)


17

Tôi nghi ngờ tôi đã mắc một lỗi học sinh ở đây và đang tìm kiếm làm rõ. Rất nhiều lớp trong giải pháp của tôi (C #) - tôi dám nói là đa số - cuối cùng tôi đã viết một giao diện tương ứng cho. Ví dụ: giao diện "ICalculator" và lớp "Máy tính" thực hiện nó, mặc dù tôi không bao giờ có thể thay thế máy tính đó bằng một triển khai khác. Ngoài ra, hầu hết các lớp này nằm trong cùng một dự án với sự phụ thuộc của chúng - chúng thực sự chỉ cần internal, nhưng cuối cùng lại trở publicthành một tác dụng phụ của việc thực hiện các giao diện tương ứng.

Tôi nghĩ rằng cách tạo giao diện này cho mọi thứ bắt nguồn từ một vài sai lầm: -

1) Ban đầu tôi nghĩ rằng một giao diện là cần thiết để tạo các thử nghiệm đơn vị (tôi đang sử dụng Moq), nhưng tôi đã phát hiện ra rằng một lớp có thể bị chế giễu nếu các thành viên của nó virtualvà nó có một hàm tạo không tham số (sửa cho tôi nếu Tôi đã sai).

2) Ban đầu tôi nghĩ một giao diện là cần thiết để đăng ký một lớp với khung IoC (Castle Windsor), vd

Container.Register(Component.For<ICalculator>().ImplementedBy<Calculator>()...

trong thực tế tôi chỉ có thể đăng ký loại bê tông chống lại chính nó:

Container.Register(Component.For<Calculator>().ImplementedBy<Calculator>()...

3) Sử dụng các giao diện, ví dụ: tham số hàm tạo để tiêm phụ thuộc, dẫn đến "khớp nối lỏng lẻo".

Vì vậy, tôi đã phát điên với các giao diện?! Tôi biết các tình huống mà bạn sẽ "bình thường" sử dụng một giao diện, ví dụ như phơi bày API công khai hoặc cho những thứ như chức năng "có thể cắm được". Giải pháp của tôi có một số lượng nhỏ các lớp phù hợp với các trường hợp sử dụng như vậy, nhưng tôi tự hỏi liệu tất cả các giao diện khác có cần thiết không, và có nên xóa không? Về điểm 3) ở trên, tôi sẽ không vi phạm "khớp nối lỏng lẻo" nếu tôi phải làm điều này chứ?

Chỉnh sửa : - Tôi chỉ chơi với Moq và dường như yêu cầu các phương thức là công khai và ảo và có một hàm tạo không tham số công khai , để có thể chế nhạo chúng. Vì vậy, có vẻ như tôi không thể có các lớp học nội bộ?


Tôi khá chắc chắn C # không yêu cầu bạn phải công khai một lớp để thực hiện giao diện, ngay cả khi giao diện đó là công khai ...
vaughandroid

1
Bạn không cần phải kiểm tra các thành viên tư nhân . Đây có thể được coi là một chỉ số của mô hình chống lớp thần. Nếu bạn cảm thấy cần phải thử nghiệm chức năng riêng tư thì thường nên gói gọn chức năng đó vào lớp riêng của nó.
Spoike

@Spoike dự án được đề cập là một dự án UI, nơi có vẻ tự nhiên để làm cho tất cả các lớp bên trong, nhưng họ vẫn cần thử nghiệm đơn vị? Tôi đã đọc ở nơi khác về việc không cần thử nghiệm các phương pháp nội bộ, nhưng tôi chưa bao giờ hiểu lý do tại sao.
Andrew Stephens

Trước tiên hãy tự hỏi đơn vị đang thử nghiệm là gì. Nó là cả lớp hay một phương thức? Đơn vị đó cần được công khai để được đơn vị kiểm tra đúng. Trong các dự án UI, tôi thường phân tách logic có thể kiểm tra thành các bộ điều khiển / mô hình xem / dịch vụ mà tất cả đều có phương thức chung. Các chế độ xem / tiện ích thực tế được nối với bộ điều khiển / mô hình xem / dịch vụ và được kiểm tra thông qua kiểm tra khói thông thường (tức là khởi động ứng dụng và bắt đầu nhấp). Bạn có thể có các kịch bản nên kiểm tra chức năng cơ bản và có một bài kiểm tra đơn vị trên các vật dụng dẫn đến các bài kiểm tra dễ vỡ và nên được thực hiện cẩn thận.
Spoike

@Spoike vậy bạn có công khai các phương thức VM của mình để làm cho chúng có thể truy cập được vào các bài kiểm tra đơn vị của chúng tôi không? Có lẽ đó là nơi tôi đang sai lầm, cố gắng hết sức để biến mọi thứ thành nội bộ. Tôi nghĩ rằng tay của tôi có thể được gắn với Moq, điều này dường như yêu cầu các lớp (không chỉ là phương thức) được công khai để chế giễu chúng (xem nhận xét của tôi về câu trả lời của Telastyn).
Andrew Stephens

Câu trả lời:


7

Nói chung, nếu bạn tạo một giao diện chỉ có một người triển khai, bạn chỉ cần viết hai lần và lãng phí thời gian của bạn. Các giao diện một mình không cung cấp khớp nối lỏng lẻo nếu chúng được kết hợp chặt chẽ với một triển khai ... Điều đó nói rằng, nếu bạn muốn chế giễu những điều này trong các bài kiểm tra đơn vị, đó thường là một dấu hiệu tuyệt vời mà bạn chắc chắn sẽ cần nhiều hơn một người thực hiện mã.

Tôi sẽ xem xét nó một chút mùi mã nếu gần như tất cả các lớp của bạn có giao diện mặc dù. Điều đó có nghĩa là hầu như tất cả họ đều làm việc với nhau bằng cách này hay cách khác. Tôi nghi ngờ rằng các lớp đang làm quá nhiều (vì không có lớp trợ giúp) hoặc bạn đã trừu tượng hóa quá nhiều (ồ, tôi muốn một giao diện nhà cung cấp cho trọng lực vì điều đó có thể thay đổi!).


1
Cảm ơn vi đa trả lơi. Như đã đề cập, có một vài lý do sai lầm tại sao tôi làm những gì tôi đã làm, mặc dù tôi biết rằng hầu hết các giao diện sẽ không bao giờ có nhiều hơn một lần thực hiện. Một phần của vấn đề là tôi đang sử dụng khung Moq, rất phù hợp cho các giao diện giả, nhưng để giả định một lớp thì nó phải công khai, có một ctr không tham số công khai và các phương thức được chế giễu phải là 'ảo công khai').
Andrew Stephens

Hầu hết các lớp tôi kiểm tra đơn vị là nội bộ và tôi không muốn công khai chúng chỉ để làm cho chúng có thể kiểm tra được (mặc dù bạn có thể tranh luận, đó là những gì tôi đã viết tất cả các giao diện này). Nếu tôi thoát khỏi các giao diện của mình thì có lẽ tôi sẽ cần tìm một khung mô phỏng mới!
Andrew Stephens

1
@AndrewStephens - không phải tất cả mọi thứ trên thế giới đều bị chế giễu. Nếu các lớp đủ vững chắc (hoặc liên kết chặt chẽ) đủ để không cần giao diện, chúng đủ vững chắc để chỉ sử dụng như trong thử nghiệm đơn vị. Thời gian / độ phức tạp của chúng không xứng đáng với lợi ích thử nghiệm.
Telastyn

-1, vì thường thì bạn không biết bạn cần bao nhiêu người triển khai khi lần đầu tiên viết mã. Nếu bạn sử dụng giao diện từ đầu, bạn không phải thay đổi ứng dụng khách khi viết thêm người triển khai.
Wilbert

1
@wilbert - chắc chắn, một giao diện nhỏ dễ đọc hơn lớp, nhưng bạn không chọn cái này hay cái kia, bạn có lớp hoặc lớp giao diện. Và bạn có thể đang đọc quá nhiều vào câu trả lời của tôi. Tôi không nói không bao giờ tạo giao diện cho một triển khai duy nhất - thực tế là hầu hết, vì hầu hết các tương tác nên yêu cầu sự linh hoạt đó. Nhưng đọc lại câu hỏi. Làm một giao diện cho tất cả mọi thứ chỉ là nuôi cấy hàng hóa. IoC không phải là một lý do. Mocks không phải là một lý do. Các yêu cầu là một lý do để thực hiện tính linh hoạt đó.
Telastyn

4

1) Ngay cả khi các lớp cụ thể có thể bị nhạo báng, nó vẫn yêu cầu bạn tạo thành viên virtualvà cung cấp một hàm tạo không tham số, có thể gây khó chịu và không mong muốn. Bạn (hoặc thành viên nhóm mới) sẽ sớm thấy mình bổ sung một cách có hệ thống virtualcác hàm tạo không tham số trong mỗi lớp mới mà không cần suy nghĩ thứ hai, chỉ vì "đó là cách thức hoạt động của công cụ".

Một giao diện là IMO một ý tưởng tốt hơn nhiều khi bạn cần chế nhạo một phụ thuộc, bởi vì nó cho phép bạn trì hoãn việc thực hiện cho đến khi bạn thực sự cần nó, và tạo ra một hợp đồng rõ ràng về sự phụ thuộc.

3) Làm thế nào mà một sự giả dối?

Tôi tin rằng các giao diện là tuyệt vời để xác định các giao thức nhắn tin giữa các đối tượng cộng tác. Có thể có trường hợp khi nhu cầu đối với họ là tranh cãi, chẳng hạn như với các thực thể miền . Tuy nhiên, bất cứ nơi nào bạn có một đối tác ổn định (nghĩa là, một phụ thuộc được tiêm thay vì tham chiếu thoáng qua) bạn cần liên lạc, bạn thường nên cân nhắc sử dụng giao diện.


3

Ý tưởng của IoC là làm cho các loại bê tông có thể thay thế. Vì vậy, ngay cả khi (ngay bây giờ) bạn chỉ có một Máy tính duy nhất thực hiện ICalculator, việc triển khai thứ hai chỉ có thể phụ thuộc vào giao diện chứ không phụ thuộc vào chi tiết triển khai từ Máy tính.

Do đó, phiên bản đầu tiên của đăng ký IoC-Container của bạn là phiên bản chính xác và là phiên bản bạn nên sử dụng trong tương lai.

Nếu bạn làm cho các lớp học của bạn công khai hoặc nội bộ không thực sự liên quan và cũng không phải là moq-ing.


2

Tôi thực sự lo lắng hơn về việc bạn dường như cảm thấy cần phải "chế giễu" mọi thứ trong toàn bộ dự án của mình.

Kiểm tra nhân đôi chủ yếu hữu ích để tách mã của bạn khỏi thế giới bên ngoài (libs của bên thứ ba, IO đĩa, IO mạng, v.v.) hoặc từ các tính toán thực sự tốn kém. Quá tự do với khung cô lập của bạn (bạn thậm chí có THỰC SỰ cần nó không? Dự án của bạn có phức tạp không?) Có thể dễ dàng dẫn đến các thử nghiệm được kết hợp với việc thực hiện và nó làm cho thiết kế của bạn cứng nhắc hơn. Nếu bạn viết một loạt các bài kiểm tra xác minh rằng một số lớp được gọi là phương thức X và Y theo thứ tự đó, sau đó truyền tham số a, b và c cho phương thức Z, bạn có THỰC SỰ nhận được giá trị không? Đôi khi bạn nhận được các bài kiểm tra như thế này khi kiểm tra ghi nhật ký hoặc tương tác với lib của bên thứ ba, nhưng đó phải là ngoại lệ, không phải là quy tắc.

Ngay khi khung cô lập của bạn nói với bạn rằng bạn phải công khai công cụ và bạn phải luôn có các hàm tạo không tham số, đã đến lúc thoát ra khỏi chỗ tránh, bởi vì tại thời điểm này, khung của bạn đang CẦN bạn viết mã xấu (tệ hơn).

Nói cụ thể hơn về giao diện, tôi thấy chúng hữu ích trong một vài trường hợp chính:

  • Khi bạn cần gửi các cuộc gọi phương thức đến các loại khác nhau, ví dụ: khi bạn có nhiều triển khai (đây là một cách rõ ràng, vì định nghĩa của một giao diện trong hầu hết các ngôn ngữ chỉ là một tập hợp các phương thức ảo)

  • Khi bạn cần có khả năng tách phần còn lại của mã khỏi một số lớp. Đây là cách thông thường để trừu tượng hóa hệ thống tệp và truy cập mạng và cơ sở dữ liệu, v.v. từ logic cốt lõi của ứng dụng của bạn.

  • Khi bạn cần đảo ngược điều khiển để bảo vệ ranh giới ứng dụng. Đây là một trong những cách mạnh mẽ nhất có thể để quản lý các phụ thuộc. Tất cả chúng ta đều biết các mô-đun cấp cao và mô-đun cấp thấp không nên biết về nhau, vì chúng sẽ thay đổi vì những lý do khác nhau và bạn KHÔNG muốn có sự phụ thuộc vào HTTPContext trong mô hình miền của bạn hoặc trên một khung kết xuất Pdf nào đó trong thư viện nhân ma trận của bạn . Làm cho các lớp ranh giới của bạn thực hiện các giao diện (ngay cả khi chúng không bao giờ được thay thế) giúp nới lỏng sự ghép nối giữa các lớp và nó làm giảm đáng kể nguy cơ phụ thuộc thấm qua các lớp từ các loại biết quá nhiều loại khác.


1

Tôi nghiêng về ý tưởng rằng, nếu một số logic là riêng tư, thì việc triển khai logic đó sẽ không gây lo ngại cho bất kỳ ai, và vì thế không nên thử nghiệm. Nếu một số logic đủ phức tạp để bạn muốn kiểm tra nó, logic đó có thể có một vai trò riêng biệt và phải được công khai. Ví dụ: nếu tôi trích xuất một số mã trong một phương thức trợ giúp riêng tư, tôi thấy rằng đó thường là thứ gì đó có thể ngồi trong một lớp công khai riêng biệt (một trình định dạng, trình phân tích cú pháp, trình ánh xạ, bất cứ điều gì).
Đối với các giao diện, tôi cho phép cần phải sơ khai hoặc chế nhạo lớp điều khiển tôi. Những thứ như repos, tôi thường muốn có thể sơ khai hoặc chế nhạo. Một người lập bản đồ trong một bài kiểm tra tích hợp, (thường) không quá nhiều.

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.