Làm thế nào là mixin hoặc đặc điểm tốt hơn so với thừa kế đơn giản?


54

C ++ có nhiều kế thừa đơn giản, nhiều thiết kế ngôn ngữ cấm nó là nguy hiểm. Nhưng một số ngôn ngữ như Ruby và PHP sử dụng cú pháp lạ để làm điều tương tự và gọi nó là mixins hoặc đặc điểm. Tôi đã nghe nhiều lần rằng mixins / tính trạng khó lạm dụng hơn so với thừa kế đơn giản.

Điều gì đặc biệt làm cho chúng ít nguy hiểm hơn? Có điều gì đó không thể xảy ra với mixins / tính trạng nhưng có thể với đa thừa kế kiểu C ++ không? Có thể chạy vào vấn đề kim cương với họ?

Điều này có vẻ như chúng ta đang sử dụng nhiều kế thừa nhưng chỉ đưa ra lý do rằng đó là những hỗn hợp / đặc điểm để chúng ta có thể sử dụng chúng.


7
Mong được ai đó đăng một câu trả lời được viết tốt, chu đáo và kỹ lưỡng cho câu hỏi này.
Robert Harvey

7
Ruby và PHP đã không giới thiệu mixin và đặc điểm. Mixins được giới thiệu trong Flavours (1980), Traits được giới thiệu trong Squeak Smalltalk (2001). Flavours là ngôn ngữ hướng đối tượng đầu tiên có nhiều sự kế thừa và nó đã sử dụng mixins. C ++ chỉ có nhiều sự kế thừa trong phiên bản 2.0, được phát hành vào năm 1989, 9 năm sau Flavours. Vì vậy, câu hỏi nên là: Hương vị có mixin đơn giản, nhưng một số ngôn ngữ như C ++ đưa ra cú pháp lạ để làm điều tương tự và gọi nó là nhiều kế thừa.
Jörg W Mittag

Câu trả lời:


31

Có một số vấn đề với nhiều kế thừa khi nó được sử dụng với các lớp đầy đủ, nhưng tất cả đều xoay quanh sự mơ hồ .

Sự mơ hồ hiện lên theo một vài cách khác nhau:

  1. Nếu bạn có hai lớp cơ sở với cùng một trường xvà loại dẫn xuất yêu cầu x, thì nó nhận được gì?
    • Nếu hai xbiến có loại không phù hợp, bạn có thể suy ra nó.
    • Nếu chúng cùng loại, bạn có thể thử hợp nhất chúng vào cùng một biến.
    • Bạn luôn có thể phơi bày chúng là những cái tên đủ điều kiện kỳ ​​lạ.
  2. Nếu bạn có hai lớp cơ sở có cùng chức năng fvới chữ ký giống hệt nhau và ai đó gọi f, cái nào được gọi?
    • Điều gì xảy ra nếu hai lớp cơ sở chia sẻ một tổ tiên ảo chung khác (vấn đề kim cương).
    • Điều gì nếu chức năng có chữ ký khác nhau, nhưng tương thích?
  3. Khi bạn xây dựng một lớp với hai lớp cơ sở, hàm tạo nào của lớp cơ sở được gọi đầu tiên? Khi bạn tiêu diệt đối tượng, cái nào bị giết?
  4. Khi bạn bố trí đối tượng trong bộ nhớ, làm thế nào để bạn thực hiện nó một cách nhất quán?
  5. Làm thế nào để bạn xử lý tất cả các trường hợp này với 3 lớp cơ sở? 10?

Và điều đó bỏ qua những thứ như công văn động, suy luận kiểu, khớp mẫu và những thứ khác mà tôi biết ít hơn sẽ trở nên khó khăn hơn khi ngôn ngữ hỗ trợ nhiều kế thừa của các lớp đầy đủ.

Các đặc điểm hoặc Mix-in (hoặc giao diện hoặc ...) đều là các cấu trúc giới hạn cụ thể các khả năng của một loại để không có sự mơ hồ. Họ hiếm khi sở hữu bất cứ thứ gì. Điều này cho phép thành phần của các loại mượt mà hơn vì không có hai biến hoặc hai hàm ... có một biến và một tham chiếu; một chức năng và một chữ ký. Trình biên dịch biết phải làm gì.

Cách tiếp cận phổ biến khác được thực hiện là buộc người dùng "xây dựng" (hoặc trộn vào) loại một tại một thời điểm. Thay vì các lớp cơ sở là đối tác bình đẳng trong loại mới, bạn thêm một loại vào loại khác - ghi đè bất cứ thứ gì có ở đó (thường với cú pháp tùy chọn để đặt lại tên và / hoặc hiển thị lại các bit bị ghi đè).

Có điều gì đó không thể xảy ra với mixins / tính trạng nhưng có thể với đa thừa kế kiểu C ++ không?

Tùy thuộc vào ngôn ngữ - thường trở nên rắc rối hoặc không thể hợp nhất việc triển khai các hàm và lưu trữ cho các biến từ nhiều lớp cơ sở và phơi bày chúng trong loại dẫn xuất.

Có thể chạy vào vấn đề kim cương với họ?

Đôi khi các biến thể ít nghiêm trọng hơn sẽ bật lên dựa trên ngôn ngữ của bạn, nhưng thường thì không. Toàn bộ điểm của các đặc điểm là để phá vỡ sự mơ hồ đó.


Bạn có thể cho tôi ví dụ về ngôn ngữ / công nghệ cho phép các kiểu "xây dựng" như vậy không?
Gherman

@german - Tôi chắc chắn không phải là chuyên gia Scala, nhưng sự hiểu biết của tôi là đó là những gì withtừ khóa làm ở đó.
Telastyn

"Các cấu trúc giới hạn cụ thể các khả năng của một loại để không có sự mơ hồ": loại giới hạn nào? Các giao diện không có các biến, vì vậy đó là một giới hạn, nhưng các đặc điểm của Scala và các mô-đun Ruby thì có. Có phải họ bị hạn chế theo những cách khác?
David Moles

@DavidMoles - đó là cách phổ biến, tôi cũng đã thấy những hạn chế đối với các quy tắc gửi ảo (nghĩ rằng các giao diện được triển khai rõ ràng của C #).
Telastyn
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.