Tại sao phải niêm phong một lớp học?


96

Tôi muốn biết động lực đằng sau phần lớn các lớp được niêm phong trong .Net framework là gì. Lợi ích của việc niêm phong một lớp học là gì? Tôi không thể hiểu được cách không cho phép kế thừa có thể hữu ích và rất có thể không phải là người duy nhất chống lại các lớp này.

Vì vậy, tại sao khung công tác được thiết kế theo cách này và nó sẽ không phải là sự thay đổi đột phá đối với mọi thứ? Phải có lý do khác mà chỉ là do ác?



Câu trả lời:


41
  • Đôi khi các lớp quá quý giá và không được thiết kế để kế thừa.
  • Runtime / Reflection có thể tạo ra các giả định kế thừa về các lớp được niêm phong khi tìm kiếm các kiểu. Một ví dụ tuyệt vời về điều này là - Các thuộc tính nên được niêm phong để tăng tốc độ thời gian chạy tra cứu. type.GetCustomAttributes (typeof (MyAttribute)) sẽ hoạt động nhanh hơn đáng kể nếu MyAttribute được niêm phong.

Bài báo MSDN cho chủ đề này là Giới hạn khả năng mở rộng bằng các lớp niêm phong .


3
Rất vui khi thấy họ nói rõ ràng là "sử dụng một cách thận trọng" bây giờ ... ước gì họ sẽ thực hành những gì họ đã giảng.
mmiika

13
Đó có vẻ như là một lời khuyên tồi cho tôi :(
Jon Skeet

4
@CVertex: Xin lỗi, tôi không cố gắng chỉ trích bạn - chỉ là bài báo.
Jon Skeet

16
@generalt: Tôi tin tưởng vào việc thiết kế để kế thừa hoặc cấm nó. Thiết kế để kế thừa tốn khá nhiều công việc và thường sẽ hạn chế việc triển khai trong tương lai. Sự kế thừa cũng tạo ra sự không chắc chắn cho người gọi về chính xác những gì họ sẽ gọi. Nó cũng không kết hợp tốt với tính bất biến (mà tôi là một fan hâm mộ). Tôi chỉ thấy kế thừa lớp hữu ích ở một số nơi tương đối nhỏ (trong khi tôi thích giao diện).
Jon Skeet,

1
@CVertex Nếu bạn đã sử dụng .NET, bạn có thể gặp phải sự cố và chỉ không nhận thấy, gần như tất cả các lớp lõi .NET đều được niêm phong.
CoryG

103

Các lớp nên được thiết kế để kế thừa hoặc cấm nó. Có một chi phí để thiết kế để thừa kế:

  • Nó có thể ghim quá trình triển khai của bạn (bạn phải khai báo phương thức nào sẽ gọi phương thức nào khác, trong trường hợp người dùng ghi đè một phương thức này chứ không phải phương thức kia)
  • Nó tiết lộ việc triển khai của bạn hơn là chỉ các hiệu ứng
  • Nó có nghĩa là bạn phải nghĩ đến nhiều khả năng hơn khi thiết kế
  • Những thứ như Bằng rất khó thiết kế trong cây kế thừa
  • Nó yêu cầu thêm tài liệu
  • Một kiểu bất biến được phân lớp có thể trở thành có thể thay đổi được (ick)

Mục 17 của Java hiệu quả đi vào chi tiết hơn về điều này - bất kể thực tế là nó được viết trong ngữ cảnh của Java, lời khuyên cũng áp dụng cho .NET.

Cá nhân tôi ước các lớp được niêm phong theo mặc định trong .NET.


26
Hmm .. nếu bạn mở rộng một lớp học, không phải là vấn đề của bạn nếu bạn phá vỡ nó?
mmiika 06-08

25
Điều gì sẽ xảy ra nếu một thay đổi triển khai mà bạn không có quyền kiểm soát trong lớp cơ sở khiến bạn bị phá vỡ? Đó là lỗi của ai? Về cơ bản, sự kế thừa giới thiệu sự mong manh. Thành phần ưu tiên hơn kế thừa thúc đẩy sự mạnh mẽ, IMO.
Jon Skeet

6
Có, giao diện đẹp - và vâng, bạn có thể ưu tiên bố cục. Nhưng nếu tôi để lộ một lớp cơ sở không được niêm phong mà không suy nghĩ cẩn thận về nó, thì tôi nên hy vọng rằng các thay đổi có thể phá vỡ các lớp dẫn xuất. Đó là một điều tồi tệ đối với tôi. Tốt hơn để niêm phong lớp và tránh vỡ, IMO.
Jon Skeet

8
@Joan: Thành phần là mối quan hệ "có-một" chứ không phải "là-a". Vì vậy, nếu bạn muốn viết một lớp có thể hoạt động giống như một danh sách theo một số cách, nhưng không phải trong những cách khác, bạn có thể muốn tạo một lớp với biến thành viên List <T>, thay vì dẫn xuất từ ​​List <T>. Sau đó, bạn sẽ sử dụng danh sách để triển khai các phương pháp khác nhau.
Jon Skeet

3
@ThunderGr: Nhưng đó là quan điểm của tôi: khi bạn không cần phải lo lắng về những triển khai khác được cung cấp bởi các lớp con, bạn có thể tự do hơn với việc triển khai của riêng mình. Ví dụ: giả sử một phương thức được thực hiện bằng cách gọi một phương thức khác và cả hai đều là ảo. Đó là nhu cầu được ghi nhận - mặc dù nó cảm thấy như một chi tiết thực hiện. Về cơ bản, thiết kế để thừa kế thêm còng tay - điều này phù hợp trong một số trường hợp, nhưng không phù hợp với những trường hợp khác. Tôi muốn có một lớp được niêm phong triển khai một giao diện hơn là một lớp không được niêm phong với một loạt các phương thức ảo.
Jon Skeet

8

Có vẻ như các nguyên tắc chính thức của Microsoft về niêm phong đã phát triển kể từ khi câu hỏi này được hỏi cách đây ~ 9 năm và họ đã chuyển từ triết lý chọn tham gia (đóng dấu theo mặc định) sang chọn không tham gia (không niêm phong theo mặc định):

X KHÔNG niêm phong các lớp học mà không có lý do chính đáng để làm như vậy.

Việc niêm phong một lớp vì bạn không thể nghĩ ra một kịch bản có thể mở rộng không phải là một lý do chính đáng. Người dùng Framework thích kế thừa từ các lớp vì nhiều lý do rõ ràng khác nhau, như thêm các thành viên tiện lợi. Xem Lớp chưa được niêm phong để biết ví dụ về những lý do rõ ràng mà người dùng muốn kế thừa từ một loại.

Những lý do chính đáng để niêm phong một lớp bao gồm những điều sau:

  • Lớp là một lớp tĩnh. Xem Thiết kế lớp tĩnh.
  • Lớp lưu trữ các bí mật nhạy cảm về bảo mật trong các thành viên được bảo vệ kế thừa.
  • Lớp kế thừa nhiều thành viên ảo và chi phí niêm phong chúng riêng lẻ sẽ lớn hơn lợi ích của việc bỏ niêm phong lớp.
  • Lớp là một thuộc tính yêu cầu tra cứu thời gian chạy rất nhanh. Thuộc tính được niêm phong có mức hiệu suất cao hơn một chút so với thuộc tính chưa được niêm phong. Xem Các thuộc tính.

X KHÔNG tuyên bố các thành viên được bảo vệ hoặc ảo trên các loại được niêm phong.

Theo định nghĩa, các kiểu niêm phong không thể được kế thừa từ. Điều này có nghĩa là không thể gọi các thành viên được bảo vệ trên các loại được niêm phong và không thể ghi đè các phương thức ảo trên các loại được niêm phong.

✓ XEM XÉT niêm phong các thành viên mà bạn ghi đè. Các vấn đề có thể xảy ra từ việc giới thiệu thành viên ảo (được thảo luận trong Thành viên ảo) cũng áp dụng cho các ghi đè, mặc dù ở mức độ nhẹ hơn một chút. Niêm phong ghi đè bảo vệ bạn khỏi những vấn đề này bắt đầu từ thời điểm đó trong hệ thống phân cấp kế thừa.

Thật vậy, nếu bạn tìm kiếm cơ sở mã ASP.Net Core , bạn sẽ chỉ tìm thấy khoảng 30 lần xuất hiện sealed class, hầu hết là thuộc tính và lớp thử nghiệm.

Tôi nghĩ rằng bảo toàn tính bất biến là một lập luận tốt ủng hộ việc niêm phong.


4

Tôi tìm thấy câu này trong tài liệu msdn: "Các lớp được niêm phong chủ yếu được sử dụng để ngăn chặn sự dẫn xuất. Bởi vì chúng không bao giờ có thể được sử dụng như một lớp cơ sở, một số tối ưu hóa thời gian chạy có thể làm cho việc gọi các thành viên lớp được niêm phong nhanh hơn một chút."

Tôi không biết liệu hiệu suất có phải là lợi thế duy nhất của các lớp kín hay không và cá nhân tôi cũng muốn biết bất kỳ lý do nào khác ...


4
Sẽ rất thú vị để xem những gì loại hiệu quả lợi ích mà họ đang nói về ...
mmiika

3

Hiệu suất là một yếu tố quan trọng, ví dụ, lớp chuỗi trong java là cuối cùng (<- Seal) và lý do cho điều này chỉ là hiệu suất. Tôi nghĩ một điểm quan trọng khác là để tránh vấn đề lớp cơ sở giòn được mô tả chi tiết tại đây: http://blogs.msdn.com/ericlippert/archive/2004/01/07/virtual-methods-and-brittle-base-classes. aspx

Nếu bạn cung cấp một khung công tác, điều quan trọng là các dự án kế thừa khả năng bảo trì và nâng cấp khung công tác của bạn để tránh vấn đề lớp cơ sở giòn


Lý do để String trong java là cuối cùng không phải là hiệu suất, mà là bảo mật.
CesarB

@CesarB: Có, nhưng cũng có thể, String không phải là một lớp Java bình thường. Đây là lớp duy nhất (tôi tin là) trong Java hỗ trợ nạp chồng toán tử (để biết thêm, xem tại đây , phần: "Ngay cả C và Java cũng có (mã cứng) nạp chồng toán tử"), điều này không thể thực hiện được trong một lớp bình thường. Vì điều này, Stringlớp thậm chí có thể không thể phân lớp, ngay cả khi nó không phải là lớp cuối cùng.
tiền ký quỹ

1

Kín được sử dụng để ngăn chặn "vấn đề lớp cơ sở giòn". Tôi đã tìm thấy một bài báo hay trên MSDN giải thích điều đó.


0

Niêm phong cho phép bạn nhận ra một số tăng hiệu suất nhỏ. Điều này ít đúng hơn trong thế giới của JIT và sự bi quan lười biếng hơn là trong thế giới của C ++, nhưng vì .NET không tốt bằng sự bi quan như các trình biên dịch java chủ yếu là do các triết lý thiết kế khác nhau nên nó vẫn hữu ích. Nó cho trình biên dịch biết rằng nó có thể gọi trực tiếp bất kỳ phương thức ảo nào thay vì gọi chúng gián tiếp thông qua vtable.

Nó cũng quan trọng khi bạn muốn có một 'thế giới khép kín' cho những thứ như so sánh bình đẳng. Thông thường, một khi tôi xác định một phương pháp ảo, tôi đã cố gắng rất nhiều để xác định một khái niệm so sánh bình đẳng thực sự triển khai ý tưởng. Mặt khác, tôi có thể định nghĩa nó cho một lớp con cụ thể của lớp bằng phương thức ảo. Niêm phong lớp đó đảm bảo rằng sự bình đẳng thực sự tồn tại.


0

Niêm phong một lớp giúp quản lý tài nguyên dùng một lần dễ dàng hơn.


0

Để xác định xem có nên niêm phong một lớp, phương thức hoặc thuộc tính hay không, bạn thường nên xem xét hai điểm sau:

• Những lợi ích tiềm năng mà các lớp dẫn xuất có thể đạt được thông qua khả năng tùy chỉnh lớp của bạn.

• Tiềm năng rằng các lớp dẫn xuất có thể sửa đổi các lớp của bạn theo cách khiến chúng không còn hoạt động chính xác hoặc như mong đợi.


0

Một xem xét thêm là các lớp niêm phong không thể được ghi lại trong các bài kiểm tra đơn vị của bạn. Từ tài liệu của Microsoft :

Các lớp niêm phong hoặc các phương thức tĩnh không thể được khai báo bởi vì các loại sơ khai dựa vào việc gửi phương thức ảo. Đối với những trường hợp như vậy, hãy sử dụng các loại miếng đệm như được mô tả trong Sử dụng miếng chêm để tách ứng dụng của bạn khỏi các cụm khác để kiểm tra đơn vị

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.