Множинне успадкування

Матеріал з Вікіпедії — вільної енциклопедії.
Перейти до навігації Перейти до пошуку

Множи́нна спадко́вість — властивість деяких обʼєктно-орієнтованих мов програмування, в яких класи можуть успадкувати поведінку і властивості більш ніж від одного суперкласу (безпосереднього батьківського класу). Це відрізняється від простого спадкування, у випадку якого клас може мати тільки один суперклас.

Мови програмування з підтримкою множинного спадкування: Eiffel, C++, Dylan, Python, Perl, Curl, Common Lisp (завдяки CLOS), OCaml, Tcl (завдяки Incremental Tcl)[1] та Object REXX (завдяки використанню класів домішок).

Огляд[ред. | ред. код]

Множинне спадкування дозволяє класу успадковувати функціональність від декількох інших класів, оскільки дозволяє класу StreetMusician успадковуватись від класів Human, Musician, Worker. Це можна скоротити як StreetMusician : Human, Musician, Worker. При множинному спадкуванні в попередньому прикладі може виникнути невизначеність, якщо, наприклад, клас Musician походить від Human і Worker, а клас Worker також походить від Human. В такому випадку кажуть про присутність ромбоподібного спадкування. Таким чином отримуємо:

Worker          :  Human
Musician        :  Human, Worker
StreetMusician  :  Human, Musician, Worker

Якщо компілятор переглядає клас StreetMusician, йому необхідно знати, коли об'єднувати однакові властивості, а коли тримати їх окремо. Наприклад, має сенс об'єднати властивості Age класу Human в StreetMusician. Вік людини не змінюється незалежно від того, чи ми розглядаємо її як музиканта, працівника або як людину загалом. З іншого боку, ім'я може бути як сценічним псевдонімом, так і справжнім ім'ям. Вибір об'єднати або відокремити покладається на програміста, який має знати, що саме є правильним при розробці певного класу.

Різні мови обробляють повторюване успадкування різними шляхами.

  • Eiffel дозволяє програмісту явно об'єднати або розділити властивості успадковані від суперкласів. Eiffel автоматично об'єднує властивості, якщо вони мають однакові імена та реалізації. Програміст має змогу перейменувати успадковані властивості, щоб розділити їх. Eiffel також дозволяє явне повторюване спадкування, таке як A: B, B.
  • C++ вимагає явної вказівки, з якого батьківського класу треба використати дану властивість, тобто "Worker::Human.Age". C++ на відміну від Eiffel не дозволяє явного повторюваного спадкування через відсутність можливості вказати, який з суперкласів треба використовувати. C++ підтримує можливість уникнення неоднозначності через створення єдиного екземпляра батьківського класу через використання механізму віртуальної спадковості (тобто "Worker::Human" і "Musician::Human" будуть вказувати на один і той самий об'єкт).
  • Perl використовує список класів для спадкування як впорядкований список. Компілятор використовує метод знайденим першим за допомогою пошуку в глибину серед списку суперкласів або C3 лінеаризації ієрархії класів. Різні розширення забезпечують альтернативні побудови. Python має таку саму структуру, але, на відміну від Perl, містить це як частину синтаксису самої мови. В Perl і Python на семантику класу впливає порядок спадкування.

Smalltalk, C#, Objective-C, Object Pascal, Java, Nemerle, та PHP не підтримують множинної спадковості реалізації, і це дозволяє уникнути будь-якої неоднозначності. Однак всі вони, крім Smalltalk, надають класам можливість реалізувати декілька інтерфейсів.

Перевіркою на необхідність множинного спадкування може стати ситуація, коли після побудови структури класів кінцеві класи (листя) можна згрупувати в набори за різними ортогональними ознаками і ці набори між собою перетинаються, що може слугувати ознакою неможливості обійтися однією структурою спадкування, в якій існували б якісь проміжні класи з потрібною поведінкою.

Алмазна проблема[ред. | ред. код]

Діаграма ромбоподібного успадкування.

«Алмазна проблема» (англ. diamond problem) (інколи згадувана як «Смертоносний діамант смерті») це неоднозначність, яка виникає, коли два класи B і C успадковуються від A, а клас D успадковується від обох B і C. Якщо в A є метод, який B і C перевизначили, а D не замістив його, тоді яку версію методу успадковує D: метод B чи метод C?

Наприклад, у контексті розробки програмного забезпечення для GUI, клас Button може успадкуватися від обох класів Rectangle (для зовнішнього вигляду) і Clickable (для функціональності/обробляння введення), а класи Rectangle і Clickable обидва успадковуються від класу Object . Тепер, якщо метод equals викликається для об'єкта Button, і такого методу немає в класі Button, але є заміщення методу equals у Rectangle або Clickable (або в обох), який метод слід викликати?

Це явище називають «алмазною проблемою» через форму діаграми успадкування класів у цій ситуації. У цьому випадку клас A знаходиться вгорі, B і C окремо під ним, а D об’єднує обидва внизу, утворюючи форму ромба.

Примітки[ред. | ред. код]

  1. Tcl Advocacy. Архів оригіналу за 22 вересня 2010. Процитовано 13 вересня 2010.