2010-09-05 27 views
7

Bạn có thể cung cấp các kịch bản khi chúng ta kế thừa từ một lớp, nó hoạt động trong một thời gian không, nhưng sau đó một cái gì đó khác thay đổi và giới thiệu một lỗi? Tôi đã đưa ra tình huống sau:Khi kế thừa từ một lớp học bạn sẽ thấy đau sau này?

  • để triển khai Hình chữ nhật có lỗ, chúng tôi kế thừa từ Hình chữ nhật lớp. Trong hàm tạo, chúng ta kiểm tra xem lỗ có nằm trong hình chữ nhật hay không
  • Sau đó, ai đó thêm phương thức mới Thay đổi kích thước thành hình chữ nhật lớp. Họ không kiểm tra nếu lỗ vẫn còn bên trong.
  • Sau khi thay đổi kích thước, chúng ta có thể có hình chữ nhật có lỗ với các lỗ bên trong hình chữ nhật, đó là một lỗi.

Các vấn đề khác tôi nên cẩn thận nếu tôi chọn sử dụng kế thừa đối tượng trong C#.

+1

Nó không phải là một cái gì đó phải cẩn thận trong C#, đó là một cái gì đó phải cẩn thận trong bất kỳ ngôn ngữ hướng đối tượng. Các lớp cơ sở nên * không bao giờ * phải lo lắng về thế hệ con cháu của chúng. – Robaticus

+0

@Robaticus: Tôi đã nói C# vì trong C++ chúng ta có thể kế thừa từ nhiều lớp, và dễ dàng hơn để gặp rắc rối. Trong C# chúng ta chỉ có thể kế thừa từ một lớp. –

+0

Tôi vẫn đứng trước những gì tôi nói. Đây là điều cần lưu ý bất kể ngôn ngữ được sử dụng. Nó không phải là duy nhất cho C#. – Robaticus

Trả lời

5

Có nhiều cách để thay đổi thành lớp cơ sở có thể ảnh hưởng đến hành vi lớp học có nguồn gốc. Điều gì xảy ra nếu tác giả đột nhiên quyết định làm cho lớp học sealed chẳng hạn?

Giống như bất kỳ giao diện nào cần phải ổn định nếu người tiêu dùng không yêu cầu sửa đổi.

"Quy tắc" chính với thừa kế là Nguyên tắc thay thế Liskov. Điều này nói rằng lớp dẫn xuất nên được thay thế cho lớp cơ sở hoặc các lớp khác bắt nguồn từ nó.

Trường hợp này vi phạm quy tắc này trên khuôn mặt của nó, vì hình chữ nhật có lỗ không phải là hình chữ nhật.

Thường thì tốt nhất bạn nên sử dụng giao diện để phân chia hành vi thành các phần có thể thực hiện hợp lý. Các giao diện như vậy thường được đặt tên với tính từ hơn là danh từ. Ví dụ, nó có ý nghĩa cho cả một hình chữ nhật và một hình chữ nhật với một lỗ được trả lại, do đó, một giao diện có thể là IRenderable. Nếu nó có thể thay đổi kích thước, bạn có thể có một số IResizable, v.v. Chúng có thể được tổng hợp thành một IShape, nhưng bạn muốn cẩn thận rằng định nghĩa Hình dạng của bạn chỉ xác định các hành vi trong miền vấn đề của bạn.

Trực tiếp bắt nguồn từ một lớp khác có thể nguy hiểm khi bạn bị ràng buộc bởi hành vi của lớp đó. Nếu bạn thực sự cần phải làm điều đó, tốt nhất bạn có thể trích xuất triển khai mà bạn cần vào một lớp cơ sở chung (ví dụ: Rectangle : RectangleImplementation, IShape).

6

Điều bạn mô tả là một trong những cạm bẫy của sự thừa kế.

Một lỗ hổng khác là phân cấp thừa kế sâu. Chủ đề Stackoverflow trên composition over inheritance.

+0

muốn tôi có thể bỏ phiếu cho điều này nhiều hơn. –

0

Không gọi phương thức ảo trong hàm tạo. Xem các bài đăng của Eric Lippert về nó (part 1part 2).

3

Điều này được gọi là brittle base class problem.

Một vấn đề tiềm năng khác với thừa kế nói chung là khi bạn muốn sử dụng lớp cơ sở của riêng mình nhưng khung yêu cầu một lớp cơ sở cụ thể (ví dụ: ContextBoundObject) thay vì giao diện.

1

Đó là lý do tại sao các nhà thiết kế ngôn ngữ C# tiên đoán đã thêm từ khóa từ khóa vào ngôn ngữ. Sử dụng một cách khôn ngoan.

Và sử dụng hợp đồng mã để kiểm tra các bất biến của bạn để nhận cảnh báo sớm về sự cố.

+0

Hans, bạn có ý gì khi "sử dụng các hợp đồng mã để kiểm tra các bất biến của bạn để nhận được cảnh báo sớm về sự cố"? –

+0

Liên kết: http://devjourney.com/blog/code-contracts-part-4-object-invariants/ –

+0

Liên kết tốt đẹp, cảm ơn! –

0

Sử dụng tốt hơn -Mẫu trang trí trong các loại trường hợp này. Điều này cung cấp cách mở rộng tốt hơn.

này có thể được thực hiện bằng cách sử dụng sơ đồ sau: alt text

Đối tượng hình chữ nhật sẽ được tạo ra và quấn vào một đối tượng HoleDecorator đó sẽ chịu trách nhiệm cung cấp các lỗ. Khi thay đổi kích thước của Rectangle được thực hiện, Resizing của HoleDecorator sẽ được gọi, điều này sẽ gọi Resize của đối tượng Rectangle đầu tiên và sau đó gọi AddBehavior cho Hole Decorator, nó sẽ xác định những gì cần làm cho lỗ khi thành phần chính là đã thay đổi kích thước.

+0

Có thể chỉ định cách triển khai Hình chữ nhật có lỗ của bạn như thế nào? –

0

Tôi nghĩ rằng điều này không liên quan đến C# hoặc thừa kế. Bất kể bạn làm gì, đây là vấn đề không thể tránh khỏi của phát triển phần mềm.

Giải pháp tốt nhất và đơn giản nhất là tạo mã của bạn hàng ngày và thực hiện unit testing.

+0

Chúng tôi luôn sử dụng các bài kiểm tra đơn vị và CI, nhưng tôi không thấy nó có thể giúp ích gì trong trường hợp này. Chúng tôi không tạo ra các bài kiểm tra đơn vị chống lại các tình huống chưa tồn tại. Đó là không có cách nào chúng tôi có thể có một bài kiểm tra đơn vị dự đoán một phương pháp Resize trước khi nó được phát triển. Bạn có thể gợi ý nó có thể xảy ra như thế nào không? –

+0

Kiểm thử đơn vị thường gọi phương thức và so sánh đầu vào và đầu ra. Tôi đang làm thử nghiệm đơn vị dựa trên kịch bản cho các vấn đề tương tự. Với ví dụ của bạn; sau khi hình chữ nhật được tạo, đối tượng được chuyển tới các phép kiểm tra hình chữ nhật (thực hiện thay đổi kích thước, di chuyển, v.v.) và vượt qua các thử nghiệm hình chữ nhật với lỗ (thực hiện kiểm tra biên) với cùng ngữ cảnh. Nó hoàn toàn phụ thuộc vào tầm nhìn của nhà phát triển và không hoàn hảo nhưng đôi khi hoạt động tốt hơn thử nghiệm phương pháp. BTW nó không thay thế thử nghiệm phương pháp, nó chỉ là một thử nghiệm bổ sung. – ertan

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