2008-08-23 32 views

Trả lời

9

Còn nếu bạn chưa chắc chắn về giao diện và không muốn bất kỳ mã nào khác tùy thuộc vào giao diện hiện tại? [Đó là ra khỏi đỉnh đầu của tôi, nhưng tôi muốn được quan tâm đến lý do khác nữa!]

Edit: Một chút googling cho như sau: http://codebetter.com/blogs/patricksmacchia/archive/2008/01/05/rambling-on-the-sealed-keyword.aspx

Trích dẫn:

Có ba lý do tại sao một lớp niêm phong được tốt hơn so với một lớp niêm phong:

  • Versioning: Khi một lớp được bịt kín ban đầu, nó có thể thay đổi để niêm phong trong f uture mà không vi phạm tính tương thích. (…)
  • Hiệu suất: (…) nếu trình biên dịch JIT nhìn thấy một cuộc gọi đến một phương pháp ảo bằng cách sử dụng một loại niêm phong, trình biên dịch JIT có thể tạo ra mã hiệu quả hơn bằng cách gọi phương thức không gần như (…)
  • Bảo mật và dự đoán: Lớp học phải bảo vệ trạng thái của chính nó và không cho phép chính nó bị hỏng. Khi một lớp không được niêm phong, một lớp dẫn xuất có thể truy cập và thao tác trạng thái của lớp cơ sở nếu bất kỳ trường dữ liệu hoặc phương thức nào mà thao tác bên trong các trường đều có thể truy nhập được và không phải riêng tư. (…)
+0

Meh. Tôi rất không đồng ý. Tại sao các lớp nên quan tâm đến con cháu của họ? Làm thế nào để niêm phong một lớp giúp quản lý toàn vẹn đối tượng? Bằng cách buộc một lập trình viên tái phát minh lại lớp học? Việc hiểu sai các nguyên tắc cơ bản về OOP là khá rõ ràng từ các biểu thức như "thao túng trạng thái của lớp cơ sở". Người ta không thao túng lớp, thay vào đó, anh ta thao túng một thể hiện của lớp. Niêm phong một lớp học không đảm bảo an toàn. Đó là một điều vô nghĩa hoàn hảo. – PJK

21

Vì việc viết các lớp được mở rộng thay thế là damn cứng và yêu cầu bạn đưa ra các dự đoán chính xác về cách người dùng trong tương lai sẽ muốn mở rộng những gì bạn đã viết.

Niêm phong lớp học của bạn buộc họ phải sử dụng bố cục, mạnh mẽ hơn nhiều.

1

Điều này có thể không áp dụng cho mã của bạn, nhưng nhiều lớp trong khuôn khổ .NET được cố định kín để không ai cố tạo lớp con. Có một số tình huống nhất định mà nội bộ phức tạp và đòi hỏi một số thứ nhất định phải được kiểm soát rất đặc biệt để người thiết kế quyết định không ai nên kế thừa lớp sao cho không ai vô tình phá vỡ chức năng bằng cách sử dụng cái gì sai.

0

Bởi vì bạn luôn muốn được trao cho một tham chiếu đến lớp học và không phải là một tham chiếu có nguồn gốc vì nhiều lý do:
i. những bất biến mà bạn có trong một số phần khác của mã số
ii. bảo mật
vv

Ngoài ra, vì đó là đặt cược an toàn liên quan đến khả năng tương thích ngược - bạn sẽ không bao giờ có thể đóng lớp thừa kế đó nếu nó không được giải phóng.

Hoặc có thể bạn không có đủ thời gian để kiểm tra giao diện mà lớp học hiển thị để đảm bảo rằng bạn có thể cho phép người khác kế thừa từ đó.

Hoặc có thể không có điểm nào (bạn thấy bây giờ) khi có lớp con.

Hoặc bạn không muốn báo cáo lỗi khi mọi người cố gắng phân lớp và không quản lý để có được tất cả chi tiết nitty-gritty - cắt giảm chi phí hỗ trợ.

1

@jjnguy

Một người dùng khác có thể muốn sử dụng lại mã của bạn bằng phụ classing lớp học của bạn. Tôi không thấy lý do để ngăn chặn điều này.

Nếu họ muốn sử dụng chức năng của lớp học, họ có thể đạt được điều đó với ngăn chặn và họ sẽ có ít mã dễ vỡ hơn.

Thành phần có vẻ thường bị bỏ qua; tất cả mọi người thường xuyên muốn nhảy vào bandwagon thừa kế. Họ không nên! Thay thế là khó khăn. Mặc định thành phần; bạn sẽ cảm ơn tôi về lâu dài.

+0

Nó phụ thuộc vào nếu có một giao diện mà các siêu lớp thực hiện. Nếu không có giao diện gần với chức năng bạn muốn mở rộng, thì bố cục sẽ không hoạt động. –

0

Đôi khi giao diện lớp học của bạn không có nghĩa là bị hít vào. Giao diện công cộng chỉ là không ảo và trong khi ai đó có thể ghi đè lên các chức năng được đặt ra, nó sẽ chỉ là sai. Có nói chung họ không nên ghi đè lên giao diện công cộng, nhưng bạn có thể đảm bảo rằng họ không bằng cách làm cho lớp không kế thừa.

Ví dụ tôi có thể nghĩ ngay bây giờ là các lớp được tùy chỉnh chứa các bản sao sâu trong .Net. Nếu bạn thừa hưởng chúng, bạn sẽ mất khả năng sao chép sâu [Tôi khá mờ trong ví dụ này, đã lâu rồi tôi không làm việc với IClonable] Nếu bạn có một lớp singelton thực sự, bạn có thể không muốn các dạng thừa kế nó xung quanh, và một lớp dữ liệu kiên trì không phải là bình thường nơi bạn muốn có rất nhiều thừa kế.

8

tôi muốn cung cấp cho bạn thông điệp này từ "Code Complete":

Inheritance - lớp con - có xu hướng việc chống lại các kỹ thuật cơ bản bắt buộc bạn phải là một lập trình viên, mà là để quản lý phức tạp. Vì lợi ích của việc kiểm soát sự phức tạp, bạn nên duy trì một thiên vị nặng nề chống lại sự thừa kế.

1

Tôi đồng ý với jjnguy ... Tôi nghĩ lý do để đóng dấu một lớp học rất ít và xa. Hoàn toàn ngược lại, tôi đã ở trong tình huống nhiều hơn một lần mà tôi muốn mở rộng một lớp, nhưng không thể vì nó đã bị phong ấn.

Như một ví dụ hoàn hảo, gần đây tôi đã tạo một gói nhỏ (Java, không phải C#, nhưng cùng nguyên tắc) để bọc chức năng xung quanh công cụ ghi nhớ. Tôi muốn có một giao diện như vậy trong các bài kiểm tra, tôi có thể bỏ qua API khách hàng memcached mà tôi đang sử dụng, và vì vậy chúng tôi có thể chuyển đổi máy khách nếu nhu cầu phát sinh (có 2 khách hàng được liệt kê trên trang chủ memcached). Ngoài ra, tôi muốn có cơ hội thay thế toàn bộ chức năng nếu nhu cầu hoặc mong muốn phát sinh (chẳng hạn như nếu các máy chủ memcached bị lỗi vì lý do nào đó, chúng tôi có thể trao đổi nóng với bộ nhớ cache cục bộ thay thế).

Tôi đã tiếp xúc với giao diện tối thiểu để tương tác với API ứng dụng khách và sẽ tuyệt vời để mở rộng lớp API ứng dụng khách và sau đó chỉ cần thêm mệnh đề thực hiện với giao diện mới của tôi. Các phương thức mà tôi có trong giao diện khớp với giao diện thực tế sau đó sẽ không cần thêm chi tiết và vì vậy tôi sẽ không phải triển khai chúng một cách rõ ràng. Tuy nhiên, lớp học đã được niêm phong, vì vậy tôi đã phải thay thế các cuộc gọi proxy đến một tham chiếu nội bộ đến lớp này. Kết quả: nhiều công việc hơn và nhiều mã hơn vì không có lý do thực sự tốt.

Điều đó nói rằng, tôi nghĩ rằng có những thời điểm tiềm năng khi bạn có thể muốn tạo lớp học được niêm phong ...và điều tốt nhất tôi có thể nghĩ là một API mà bạn sẽ gọi trực tiếp, nhưng cho phép khách hàng thực hiện. Ví dụ: một trò chơi mà bạn có thể lập trình chống lại trò chơi ... nếu các lớp học của bạn không được niêm phong, thì những người chơi đang thêm các tính năng có khả năng khai thác API cho lợi thế của họ. Đây là một trường hợp rất hẹp, và tôi nghĩ rằng bất cứ lúc nào bạn có toàn quyền kiểm soát codebase, có rất ít lý do để làm cho một lớp học bị phong ấn. Đây là một trong những lý do tôi thực sự thích ngôn ngữ lập trình Ruby ... ngay cả các lớp cốt lõi được mở, không chỉ để mở rộng mà còn để thêm chức năng THAY ĐỔI VÀ THAY ĐỔI, ĐẾN ITSELF CLASS! Nó được gọi là monkeypatching và có thể là một cơn ác mộng nếu bị ngược đãi, nhưng thật vui khi chơi với!

0

Không phải mọi thứ quan trọng trong lớp đều được khẳng định dễ dàng trong mã. Có thể có ngữ nghĩa và mối quan hệ hiện tại dễ dàng bị phá vỡ bởi kế thừa và phương pháp ghi đè. Việc ghi đè một phương pháp tại một thời điểm là một cách dễ dàng để thực hiện việc này. Bạn thiết kế một lớp/đối tượng như là một thực thể có ý nghĩa duy nhất và sau đó một người nào đó đến và nghĩ rằng nếu một hoặc hai phương pháp là 'tốt hơn' nó sẽ không gây hại. Điều đó có thể hoặc không thể là sự thật. Có lẽ bạn có thể phân tách chính xác tất cả các phương thức giữa riêng tư và không riêng tư hoặc ảo và không ảo nhưng điều đó vẫn có thể không đủ. Yêu cầu thừa kế của tất cả các lớp cũng đặt thêm gánh nặng lớn cho nhà phát triển ban đầu để thấy trước tất cả các cách mà lớp kế thừa có thể làm cho mọi thứ trở nên tồi tệ.
Tôi không biết giải pháp hoàn hảo nào. Tôi thông cảm để ngăn chặn sự thừa kế nhưng đó cũng là một vấn đề vì nó cản trở việc kiểm tra đơn vị.

+0

Thừa kế không nên liên quan đến tác giả của lớp gốc; anh ta không phải là người sẽ gặp rắc rối với một lớp kế thừa vặn vẹo mọi thứ. Nếu nhà phát triển có thể thấy trước những mối quan tâm tiềm năng như vậy, thì lớp ban đầu cũng không được thiết kế tốt ở nơi đầu tiên / –

0

Tôi tiếp xúc với giao diện tối thiểu để tương tác với API ứng dụng khách và sẽ tuyệt vời để mở rộng lớp API ứng dụng khách và sau đó chỉ cần thêm mệnh đề thực hiện với giao diện mới của tôi. Các phương thức mà tôi có trong giao diện khớp với giao diện thực tế sau đó sẽ không cần thêm chi tiết và vì vậy tôi sẽ không phải triển khai chúng một cách rõ ràng. Tuy nhiên, lớp học đã được niêm phong, vì vậy tôi đã phải thay thế các cuộc gọi proxy đến một tham chiếu nội bộ đến lớp này. Kết quả: nhiều công việc hơn và nhiều mã hơn vì không có lý do thực sự tốt.

Vâng, có một lý do: mã của bạn hiện được phần nào cách ly khỏi những thay đổi đối với giao diện được ghi nhớ.

0

Hiệu suất:. (...) nếu trình biên dịch JIT thấy một cuộc gọi đến một phương pháp ảo sử dụng một loại niêm phong, trình biên dịch JIT có thể sản xuất mã hiệu quả hơn bằng cách gọi phương pháp này không hầu (...)

Đó thực sự là một lý do tuyệt vời. Do đó, đối với các lớp quan trọng về hiệu suất, sealed và bạn bè có ý nghĩa.

Tất cả các lý do khác mà tôi đã thấy được đề cập đến nay cho đến khi "không ai chạm vào lớp học của tôi!". Nếu bạn lo lắng ai đó có thể hiểu sai nội bộ của nó, bạn đã làm một công việc nghèo tài liệu đó. Bạn không thể biết rằng không có gì hữu ích để thêm vào lớp học của bạn, hoặc bạn đã biết mọi trường hợp sử dụng có thể tưởng tượng được. Ngay cả khi bạn đúng và nhà phát triển khác không nên sử dụng lớp học của bạn để giải quyết vấn đề của họ, việc sử dụng từ khóa không phải là cách tuyệt vời để ngăn chặn sai lầm như vậy. Tài liệu là. Nếu họ bỏ qua tài liệu, mất mát của họ.

2

Việc sử dụng hợp pháp duy nhất của kế thừa là xác định một trường hợp cụ thể của một lớp cơ sở như, ví dụ, khi kế thừa từ hình dạng để lấy được vòng tròn. Để kiểm tra cái nhìn này về mối quan hệ theo hướng ngược lại: là một hình dạng một sự tổng quát của Circle? Nếu câu trả lời là có thì nó là ok để sử dụng thừa kế.

Vì vậy, nếu bạn có một lớp học mà không thể có bất kỳ trường hợp cụ thể nào chuyên về hành vi của nó thì cần được niêm phong.Ngoài ra do LSP (Nguyên tắc thay thế Liskov) người ta có thể sử dụng lớp dẫn xuất nơi lớp cơ sở mong đợi và điều này thực sự áp đặt tác động lớn nhất từ ​​việc sử dụng thừa kế: mã sử dụng lớp cơ sở có thể được cấp một lớp kế thừa và nó vẫn còn phải hoạt động như mong đợi. Để bảo vệ mã bên ngoài khi không có nhu cầu rõ ràng cho các lớp con, bạn đóng dấu lớp và các máy khách của nó có thể tin rằng hành vi của nó sẽ không bị thay đổi. Nếu không, mã bên ngoài cần phải được thiết kế rõ ràng để mong đợi các thay đổi có thể có trong hành vi trong các lớp con.

Ví dụ cụ thể hơn sẽ là mẫu Singleton. Bạn cần phải đóng dấu singleton để đảm bảo người ta không thể phá vỡ "singletonness".

1

Từ góc độ hướng đối tượng, niêm phong một lớp rõ ràng ghi rõ ý định của tác giả mà không cần nhận xét. Khi tôi niêm phong một lớp, tôi đang cố gắng nói rằng lớp học này được thiết kế để đóng gói một số phần kiến ​​thức cụ thể hoặc một số dịch vụ cụ thể. Nó không có nghĩa là để được tăng cường hoặc phân lớp thêm.

Điều này cũng phù hợp với mẫu thiết kế Phương thức mẫu. Tôi có một giao diện cho biết "Tôi thực hiện dịch vụ này". Sau đó tôi có một lớp thực hiện giao diện đó. Nhưng, nếu thực hiện dịch vụ đó dựa vào ngữ cảnh mà lớp cơ sở không biết (và không nên biết)? Điều gì xảy ra là lớp cơ sở cung cấp các phương thức ảo, được bảo vệ hoặc riêng tư, và các phương thức ảo này là các móc cho các lớp con cung cấp mẩu thông tin hoặc hành động mà lớp cơ sở không biết và không thể biết. Trong khi đó, lớp cơ sở có thể chứa mã phổ biến cho tất cả các lớp con. Các lớp con này sẽ được niêm phong bởi vì chúng có nghĩa là hoàn thành một và chỉ một triển khai cụ thể của dịch vụ.

Bạn có thể tạo lập luận rằng các lớp con này sẽ được phân lớp thêm để nâng cao chúng? Tôi sẽ nói không vì nếu lớp con đó không thể hoàn thành công việc ngay từ đầu thì nó không bao giờ được bắt nguồn từ lớp cơ sở. Nếu bạn không thích nó thì bạn có giao diện gốc, hãy viết lớp thực hiện của riêng bạn.

Niêm phong các lớp con này cũng không khuyến khích mức độ thừa kế sâu, hoạt động tốt cho các khung công tác GUI nhưng hoạt động kém cho các lớp logic nghiệp vụ.

0

Hầu hết các câu trả lời (khi được trừu tượng) nêu rõ các lớp được đóng kín/hoàn tất là công cụ để bảo vệ các lập trình viên khác khỏi những sai lầm tiềm ẩn. Có một đường mờ giữa sự bảo vệ có ý nghĩa và hạn chế vô nghĩa. Nhưng miễn là lập trình viên là một trong những người được dự kiến ​​sẽ hiểu chương trình, tôi thấy không có lý do nào để hạn chế anh ta từ tái sử dụng các bộ phận của một lớp. Hầu hết các bạn nói về các lớp học. Nhưng đó là tất cả về các đối tượng!

Trong bài đăng đầu tiên của mình, DrPizza tuyên bố rằng việc thiết kế lớp kế thừa có nghĩa là dự đoán các phần mở rộng có thể có. Tôi có đúng không khi bạn nghĩ rằng lớp học nên được thừa hưởng chỉ khi nó có khả năng được mở rộng tốt? Có vẻ như bạn đã quen với việc thiết kế phần mềm từ các lớp trừu tượng nhất. Cho phép tôi giải thích ngắn gọn về cách suy nghĩ khi thiết kế:

Bắt đầu từ các đối tượng rất cụ thể, tôi tìm thấy các đặc điểm và chức năng [do đó] chúng có chung và tôi trừu tượng hóa nó thành siêu lớp của các đối tượng cụ thể đó. Đây là một cách để giảm trùng lặp mã.

Trừ khi phát triển một số sản phẩm cụ thể như khuôn khổ, tôi nên quan tâm đến mã số của tôi, chứ không phải mã khác (ảo). Thực tế là những người khác có thể thấy hữu ích khi sử dụng lại mã của tôi là một phần thưởng tốt đẹp, không phải mục tiêu chính của tôi. Nếu họ quyết định làm như vậy, đó là trách nhiệm của họ để đảm bảo tính hợp lệ của các phần mở rộng. Điều này áp dụng cho toàn đội. Thiết kế phía trước rất quan trọng đối với năng suất.

Quay lại ý tưởng của tôi: Các đối tượng của bạn chủ yếu phục vụ cho mục đích của bạn, không phải là một số chức năng có thể là /aa/cana của các loại phụ của chúng. Mục tiêu của bạn là giải quyết vấn đề đã cho. Các ngôn ngữ hướng đối tượng sử dụng thực tế rằng nhiều vấn đề (hoặc nhiều khả năng các khái niệm con của chúng) là tương tự nhau và do đó mã hiện có có thể được sử dụng để đẩy nhanh phát triển hơn nữa.

Niêm phong một lớp học buộc những người có thể tận dụng lợi thế của mã hiện tại KHÔNG CÓ THỰC HIỆN SỬA ĐỔI SẢN PHẨM CỦA BẠN để phát minh lại bánh xe. (Đây là một ý tưởng quan trọng của luận án của tôi: Thừa kế một lớp học không sửa đổi nó! Mà dường như khá cho người đi bộ và rõ ràng, nhưng nó thường bị bỏ qua).

Mọi người thường sợ rằng các lớp "mở" của họ sẽ bị xoắn vào thứ gì đó không thể thay thế được phần tử của nó. Vậy cái gì? Tại sao bạn cần quan tâm? Không công cụ nào có thể ngăn cản người lập trình xấu tạo ra phần mềm xấu!

Tôi không cố gắng biểu thị các lớp có thể kế thừa là cách thiết kế cuối cùng chính xác, hãy xem xét điều này giống như lời giải thích về khuynh hướng của tôi đối với các lớp kế thừa. Đó là vẻ đẹp của lập trình - hầu như vô hạn các giải pháp chính xác, mỗi giải pháp đều có khuyết điểm và ưu điểm riêng. Nhận xét và đối số của bạn được hoan nghênh.

Và cuối cùng, câu trả lời của tôi cho câu hỏi ban đầu: Tôi muốn hoàn thành một lớp để cho người khác biết rằng tôi coi lớp là lá của cây cấp bậc và tôi thấy hoàn toàn không có khả năng nó có thể trở thành nút cha. (Và nếu bất cứ ai nghĩ rằng nó thực sự có thể, sau đó hoặc là tôi đã sai hoặc họ không nhận được tôi).

Các vấn đề liên quan