Tại sao không phải tất cả phương thức ảo hoặc tại sao không có mỗi lớp ít nhất một giao diện?


8

Đây là câu hỏi triết học hơn, giải quyết nền tảng .NET, nhưng có lẽ nó cũng hữu ích cho các ngôn ngữ khác. Tôi đang thực hiện nhiều Thử nghiệm đơn vị và đặc biệt là khi tôi đang sử dụng các thành phần của bên thứ ba mà tôi thường gặp khó khăn. Trong .NET là yêu cầu rất lớn đối với thiết kế thành phần (er) để lựa chọn phương thức nào nên ảo hay không. Một mặt là việc sử dụng thành phần (điều có ý nghĩa là ảo), mặt khác là sự nhạo báng thành phần . Bạn có thể sử dụng Shims để giả lập các thành phần của bên thứ ba nhưng điều này thường dẫn đến thiết kế xấu và phức tạp. Như tôi có thể nhớ các cuộc thảo luận về việc làm cho tất cả các phương thức là ảo (JAVA có từ khi bắt đầu) trong .NET là về hiệu suất. Nhưng nó vẫn là vấn đề? Tại sao không phải tất cả các phương thức trong .NET ảo hoặc tại sao không có mỗi lớp ít nhất một giao diện?


1
À vâng vì sau vài phút ai đó đã hạ bệ tôi (lần đầu tiên của tôi) tôi muốn biết tại sao.
Anton Kalcik

Tôi đoán người đã bỏ phiếu để đóng câu hỏi này là "quá rộng" cũng đã bỏ phiếu cho bạn.
David Arno

1
"Lập trình viên Stack Exchange là một trang web câu hỏi và trả lời cho các lập trình viên chuyên nghiệp quan tâm đến các câu hỏi khái niệm về phát triển phần mềm" Tôi có hiểu điều gì sai không?
Anton Kalcik

5
Tôi có xu hướng đồng ý rằng câu hỏi này quá rộng. "Tại sao mỗi lớp không có ảo?" Tại sao nên làm thế? Bạn đề cập đến thử nghiệm. Có lẽ bạn có thể cải thiện câu hỏi bằng cách thay đổi nó thành "tại sao lớp này (lớp .net khó chịu nhất) không có các phương thức này được đánh dấu ảo?" Tuy nhiên, bạn có thể phát hiện ra rằng câu trả lời là "Ồ đúng rồi. Họ không thực sự lường trước được sự bùng nổ của TDD khi thiết kế .NET 1.0"
cầu toàn vào

4
Nếu bạn muốn làm cho nó rộng ra bởi vì bạn chỉ quan tâm đến ý kiến, thì nó lạc đề vì hai lý do: quá rộngdựa trên ý kiến .
Jörg W Mittag

Câu trả lời:


7

Như Anders nói , một phần về hiệu suất và một phần về việc khóa các thiết kế kém suy nghĩ để giảm phạm vi rắc rối gây ra sau đó bởi những người mù quáng kế thừa những thứ không được thiết kế để kế thừa.

Hiệu năng có vẻ rõ ràng - trong khi hầu hết các phương pháp sẽ không được chú ý trên phần cứng hiện đại, một bộ thu ảo có thể gây ra một điểm nhấn đáng chú ý, ngay cả trên phần cứng hiện đại phụ thuộc nhiều hơn vào các hướng dẫn dễ dự đoán trong đường ống CPU.

Bây giờ lý do để làm cho mọi thứ ảo theo mặc định dường như chỉ được điều khiển bởi một điều duy nhất: các khung kiểm tra đơn vị. Dường như với tôi rằng đây là một lý do kém, nơi chúng tôi đang thay đổi mã để phù hợp với khung hơn là thiết kế nó đúng cách.

Có lẽ vấn đề là với các khung được sử dụng ở đây, họ nên cải thiện để cho phép thay thế các hàm trong thời gian chạy bằng các miếng chêm được tiêm thay vì xây dựng mã thực hiện điều này (với tất cả các hack để có được các phương thức riêng tư cũng như các phương thức không ảo, không đề cập đến chức năng tĩnh và bên thứ 3)

Vì vậy, các phương thức lý do không phải là ảo theo mặc định là như Anders đã nói, và bất kỳ mong muốn biến chúng thành ảo để phù hợp với một số công cụ kiểm tra đơn vị là đặt giỏ hàng trước ngựa, thiết kế phù hợp sẽ vượt qua các ràng buộc nhân tạo.

Bây giờ bạn có thể giảm thiểu tất cả những điều này bằng cách sử dụng các giao diện hoặc bằng cách làm lại toàn bộ cơ sở mã của bạn để sử dụng các thành phần (hoặc microservice) giao tiếp qua tin nhắn thay vì các cuộc gọi phương thức có dây trực tiếp. Nếu bạn phóng to bề mặt của một đơn vị để kiểm tra thành một thành phần và thành phần đó hoàn toàn khép kín, bạn không thực sự cần phải bắt vít với đơn vị đó để kiểm tra đơn vị đó.


Tôi không thể đồng ý nhiều hơn.
Robert Harvey

@gbjbaanb Tôi không hiểu câu cuối "Nếu bạn phóng to bề mặt của một đơn vị để thử nghiệm thành một thành phần và thành phần đó hoàn toàn khép kín, bạn không thực sự cần phải bắt vít với đơn vị đó để kiểm tra đơn vị đó. "
Anton Kalcik

Câu trả lời tuyệt vời (tốt hơn tôi, tôi cảm thấy :)
David Arno

1
@AntonKalcik Ý tôi là, nếu bạn có một hộp đen thì nó không nên có sự phụ thuộc cần chế giễu. Tức là, bạn kiểm tra nó bằng cách bắn đầu vào và xem những gì đi ra. Nếu hộp của bạn nhỏ và bạn đã thiết kế để tách rời khỏi các dịch vụ khác thì điều này có thể dễ dàng xảy ra. Vấn đề là một lớp không thể tách rời khỏi các lớp khác rất dễ dàng, và do đó đòi hỏi phải chế giễu. Nếu bạn nghĩ về một đơn vị là một thành phần (có thể là dll hoặc microservice) chứ không phải là một lớp, bạn có thể kiểm tra theo cách này.
gbjbaanb

Về hiệu năng: CPU có thể dễ dàng dự đoán cuộc gọi phương thức ảo (nếu nó luôn giống nhau). Vấn đề lớn là trình biên dịch JIT khó hơn nhiều đối với các cuộc gọi phương thức ảo nội tuyến, sau đó ngăn chặn tối ưu hóa hơn nữa.
Svick

6

Có hai yếu tố cho câu hỏi của bạn, vì vậy tôi sẽ thử và lần lượt giải quyết chúng:

  1. Tại sao các phương thức .NET không ảo theo mặc định?

    Kế thừa, trong thực tế, có nhiều vấn đề . Vì vậy, nếu nó được sử dụng như một phần của thiết kế, thì nó cần được xem xét cẩn thận. Bằng cách làm cho các phương thức không ảo theo mặc định, lý thuyết là nó khuyến khích nhà phát triển nghĩ về sự kế thừa khi thiết kế một lớp. Cho dù điều đó làm việc trong thực tế là một vấn đề khác của khóa học.

  2. Tại sao tất cả các lớp không thực hiện một giao diện?

    Thiết kế kém là câu trả lời đơn giản. Mặc dù thường được công nhận là thông lệ tốt trong một thời gian rất dài, nhưng thật đáng buồn là nhiều người dân vẫn không thiết kế hệ thống của họ với DI và TDD trong tâm trí.

Sự kết hợp của các lớp không được kế thừa hoàn toàn và không dễ bị chế giễu sẽ tạo ra một "cơn bão hoàn hảo" của mã khó kiểm tra. Cho đến khi chúng ta đến ngày mặc dù khi mọi người sử dụng DI và nguyên tắc thiết kế giao diện, sẽ không có nhiều việc có thể làm để giảm bớt nỗi đau.


3
"Thiết kế kém là câu trả lời đơn giản" - Tôi có xu hướng đồng ý, nhưng vẫn có ngoại lệ. Xem các đối tượng chuyển dữ liệu hoặc ghi đối tượng. Đối với ngôn ngữ POCO / POJO / insert đơn giản ở đây, các đối tượng không có ý nghĩa đối với chúng để thực hiện interfacechỉ vì. Giá trị nào có một interfacecho những gì về cơ bản là một tuple vinh quang có? (Mặc dù có thể sử dụng POJO / POCO là một lỗi thiết kế)
Dan Pantry

2
@AntonKalcik, nhóm C # áp dụng cách tiếp cận "không bao giờ đưa ra thay đổi đột phá" đối với ngôn ngữ và khiến giao diện bắt buộc chắc chắn sẽ là một thay đổi đột phá. Vì vậy, tôi nghi ngờ bạn sẽ không thấy điều đó được thêm vào C #.
David Arno

3
@DanPantry, các đối tượng dữ liệu đơn giản hiếm khi cần chế nhạo, vì vậy, thông thường không cần giao diện.
David Arno

2
Trong C♯, giao diện xác định Đối tượng, các lớp xác định Kiểu dữ liệu trừu tượng. (Đó là một quan niệm sai lầm phổ biến rằng các lớp là những gì bạn sử dụng trong C♯ để viết mã hướng đối tượng. Hoàn toàn ngược lại: Mã OO trong C♯ không bao giờ có thể sử dụng các lớp hoặc cấu trúc như các loại, chỉ giao diện. , bạn không còn làm OO nữa.) Vì vậy, tôi sẽ không gọi việc không sử dụng giao diện là lỗi thiết kế. Đó là một lựa chọn thiết kế: ADT và Đối tượng có điểm mạnh và điểm yếu kép. Bạn nên chọn những gì có ý nghĩa hơn trong tên miền cụ thể của bạn.
Jörg W Mittag

5
@ JörgWMittag: As soon as you use classes as types, you are no longer doing OO - Các lớp học loại. Gì?? Kế thừa lớp bình thường OO, bất kể bạn nói gì. Bạn không được quyết định đó không phải là OO nữa chỉ vì DI là tất cả cơn thịnh nộ.
Robert Harvey
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.