Một lớp nên biết về các lớp con của nó? Một lớp có nên làm một cái gì đó cụ thể cho một lớp con cụ thể không?
Bản năng của tôi nói với tôi rằng đó là một thiết kế tồi, nó có vẻ giống như một kiểu chống mẫu nào đó.
Một lớp nên biết về các lớp con của nó? Một lớp có nên làm một cái gì đó cụ thể cho một lớp con cụ thể không?
Bản năng của tôi nói với tôi rằng đó là một thiết kế tồi, nó có vẻ giống như một kiểu chống mẫu nào đó.
Câu trả lời:
Câu trả lời ngụ ý của khái niệm các lớp là "không".
Bất kỳ hành động, dữ liệu hoặc quan hệ nào bạn đang xử lý là một phần của tất cả các lớp con - sau đó nó sẽ được xử lý trong lớp cha mà không cần kiểm tra loại thực tế. Hoặc nó chỉ áp dụng cho một số lớp con - sau đó bạn phải thực hiện kiểm tra loại thời gian chạy để thực hiện đúng, siêu lớp sẽ phải được thay đổi bất cứ khi nào người khác thừa hưởng từ nó (hoặc nó có thể âm thầm làm sai), những thay đổi trong các lớp dẫn xuất có thể phá vỡ lớp cha không thay đổi, v.v.
Nói tóm lại, bạn nhận được một số hậu quả xấu thường đủ tệ để loại bỏ các giải pháp đó ra khỏi tầm tay. Nếu một vài trong số các lớp con của bạn làm điều tương tự và bạn muốn tránh sao chép mã (thực tế luôn luôn là một điều tốt), một giải pháp tốt hơn là giới thiệu một lớp trung cấp mà tất cả các lớp con đó có thể kế thừa mã.
Không chỉ không nên biết, nó chỉ đơn giản là không thể ! Thông thường, một lớp học có thể được mở rộng mọi lúc, mọi nơi. Nó có thể được mở rộng bởi các lớp thậm chí không tồn tại khi nó được viết.
Một số ngôn ngữ cho phép mở rộng các lớp được điều khiển bởi siêu lớp. Trong Scala, một lớp có thể được đánh dấu là sealed
, điều đó có nghĩa là nó chỉ có thể được mở rộng bởi các lớp khác trong cùng một đơn vị biên dịch (tệp nguồn). Tuy nhiên, trừ khi các lớp con đó cũng sealed
hoặc final
, các lớp con sau đó có thể được mở rộng thêm bởi các lớp khác.
Trong Scala, điều này được sử dụng để mô hình các kiểu dữ liệu đại số đóng, vì vậy List
kiểu Haskell chính tắc :
data List a = Nil | Cons a (List a)
có thể được mô hình hóa trong Scala như thế này:
sealed trait List[+A]
case object Nil extends List[Nothing]
final case class Cons[+A] extends List[A]
Và bạn có thể đảm bảo rằng chỉ có hai phụ "lớp học" tồn tại vì List
là sealed
và do đó không thể được bên ngoài mở rộng của tập tin, Cons
là final
và do đó không thể được mở rộng ở tất cả và Nil
là một object
trong đó không thể được mở rộng anyway.
Nhưng đây là trường hợp sử dụng cụ thể (mô hình hóa các kiểu dữ liệu đại số thông qua kế thừa) và ngay cả trong trường hợp này, siêu lớp thực sự không biết về các lớp con của nó. Đó là nhiều hơn một đảm bảo cho người sử dụng của các List
loại rằng nếu anh thực hiện một sự phân biệt trường hợp Nil
và Cons
, sẽ không có bất kỳ khác thay thế nảy lên sau lưng.
abstract internal
thành viên.
Câu trả lời đơn giản là không có.
Nó làm cho mã dễ vỡ và làm hỏng hai nguyên tắc cơ bản của lập trình hướng đối tượng.
Vâng, thỉnh thoảng. Ví dụ, khi có một số lượng các lớp con giới hạn tồn tại. Một mô hình khách truy cập là một minh họa về tính hữu ích của phương pháp này.
Ví dụ: tất cả các nút cây cú pháp trừu tượng (AST) của một số ngữ pháp được xác định rõ đều có thể được kế thừa từ một Node
lớp duy nhất thực hiện một mẫu khách truy cập để xử lý tất cả các loại nút.
Node
nhiên, lớp cơ sở không chứa bất kỳ tham chiếu trực tiếp nào đến các lớp con của nó. Nhưng nó chứa một accept
phương thức với một Visitor
tham số. Và Visitor
chứa một visit
phương thức cho mỗi lớp con. Vì vậy, mặc dù Node
không có tài liệu tham khảo trực tiếp đến các lớp con của nó, nó "biết" về chúng một cách gián tiếp, thông qua Visitor
giao diện. Tất cả họ cùng nhau thông qua nó.
Nếu tôi viết một thành phần cho một công ty và sau đó, sau khi tôi rời đi, ai đó sẽ mở rộng nó cho mục đích riêng của họ, tôi có nên được thông báo về điều đó không?
Không!
Tương tự với các lớp học. Tin vào bản năng của bạn.