Опущення спадкування в мовах програмування


10

Я розробляю власну мову програмування. Це мова загального призначення (думаю, що настільно набраний Python для робочого столу, тобто int x = 1;), не призначена для хмари.

Ви вважаєте, що добре не допускати спадкування чи Мікінс? (з огляду на те, що користувач хоч би мав інтерфейси)

Наприклад: Google Go, системна мова, яка шокувала спільноту програмування, не дозволяючи успадкування.

Відповіді:


4

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

Успадкування реалізації завжди може бути замінено успадкуванням інтерфейсу плюс складом, і більш сучасні розробки програмного забезпечення, як правило, йдуть у напрямку використання успадкування все менше, на користь композиції.

Так що так , не надання спадщини зокрема є цілком справедливим і дуже модним дизайнерським рішенням.

Mixins, з іншого боку, навіть не є (поки що) основною мовою, і мови, які надають функцію під назвою "mixin", часто розуміють дуже різні речі. Сміливо надайте це чи ні. Особисто я вважаю це дуже корисним, але реалізувати це правильно, можливо, дуже важко.


13

Обґрунтування того, чи є наслідування (чи якась одна особливість насправді) необхідною чи ні, не враховуючи також, що решта мовної семантики є безглуздим; ти сперечаєшся у вакуумі.

Вам потрібна послідовна філософія дизайну мови; мова повинна вміти елегантно вирішувати проблеми, для яких вона розроблена. Модель для досягнення цього може вимагати або не вимагати спадкування, але важко судити про це без великої картини.

Якщо, наприклад, у вашій мові функціонують першокласні функції, часткове функціонування функцій, поліморфні типи даних, змінні типу та загальні типи, ви в значній мірі охопили ті самі основи, що і у класичного спадкування OOP, але використовуєте іншу парадигму.

Якщо у вас є пізні прив’язки, динамічне введення тексту, методи як властивості, аргументи гнучких функцій та функції першого класу, ви також охоплюєте ті ж підстави, але знову ж таки, використовуючи іншу парадигму.

(Пошук прикладів для двох викладених парадигм залишається вправою для читача.)

Отже, подумайте, яку саме семантику ви хочете, пограйте з ними і подивіться, чи достатньо їх без успадкування. Якщо їх немає, ви можете або вирішити кинути спадщину в суміш, або можете вирішити, що чогось іншого немає.


9

Так.

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

Якщо у вас є динамічний набір тексту - і я припускаю, що ви це робите - у вас може бути поліморфізм і без успадкування. Крім того, якщо ви дозволяєте додавати довільні методи до об'єктів під час виконання, вам не дуже потрібні речі, такі як змішані.

Ще один варіант - який, на мою думку, є хорошим - це емуляція JavaScript та використання прототипів. Вони простіші, ніж заняття, але також дуже гнучкі. Просто щось врахувати.

Отже, все сказано, я б пішов на це.


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

Ні. Це статично і сильно набрано, я згадаю це вгорі.
Крістофер

Оскільки він набраний статично, ви не можете просто додавати випадкові методи до об'єктів під час виконання. Однак ви все одно можете робити набір качок: ознайомтеся з прикладом протоколів Gosu . Протягом літа я трохи використовував протоколи, і вони були дуже корисні. У Gosu також є "удосконалення", які є способом додавання методів до класів після факту. Ви також можете додати щось подібне.
Тихон Єлвіс

2
Будь ласка, досить, будь ласка, перестаньте стосуватися спадкування та повторного використання коду. Давно відомо, що успадкування - це дійсно поганий інструмент для використання коду.
Ян Худек

3
Спадкування - це лише один інструмент для повторного використання коду. Часто це дуже хороший інструмент, а інший раз - жахливий. Віддавати перевагу складу над спадщиною не означає взагалі ніколи не використовувати спадщину.
Майкл К

8

Так, це абсолютно розумне дизайнерське рішення пропустити спадщину.

Насправді є дуже вагомі причини для видалення успадкування реалізації, оскільки це може створити надзвичайно складний і важкий для підтримки код. Я б навіть пішов так далеко, щоб розцінювати спадщину (як це зазвичай реалізується в більшості мов OOP) як неправильну поведінку.

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

Ось відео, яке мені виявилося дуже просвітницьким на цю загальну тему, де Річ Хікі визначає основні джерела складності в мовах програмування (включаючи успадкування) та представляє альтернативи для кожної: Простота легко


У багатьох випадках успадкування інтерфейсу є більш підходящим. Але якщо використовувати їх при помірності, успадкування реалізації може дозволити видалення безлічі кодових шаблонів, і це найелегантніше рішення. Просто потрібні програмісти, які розумніші та уважніші, ніж середня мавпа Python / JavaScript.
Ерік Алапяа

4

Коли я вперше зіткнувся з тим, що класи VB6 не підтримують успадкування (лише інтерфейси), це мене справді дратувало (і все ще робить).

Однак причина настільки погана в тому, що у нього не було параметрів конструктора, тому ви не можете зробити звичайне введення залежності (DI). Якщо у вас є DI, то це важливіше, ніж успадкування, оскільки ви можете слідувати принципу переваги композиції над спадщиною. Це кращий спосіб повторного використання коду в будь-якому випадку.

Не маючи Mixins, хоча? Якщо ви хочете реалізувати інтерфейс і делегувати всю роботу цього інтерфейсу об'єкту, встановленому за допомогою введення залежності, тоді Mixins ідеально підходить. В іншому випадку вам доведеться записати весь код на панелі котла, щоб делегувати кожен метод та / або властивість дочірньому об'єкту. Я роблю це багато (завдяки C #), і хотілося б, щоб я цього не робив.


Так, що спонукало це питання - це реалізація SVG DOM, де повторне використання коду дуже корисно.
Крістофер

2

Щоб не погодитися з іншою відповіддю: ні, ви викидаєте функції, коли вони несумісні з чимось, чого ви хочете більше. Java (та інші мови GC'd) викинули явне управління пам’яттю, оскільки більше хотіли безпеки типу. Haskell викинув мутацію, бо хотів більше рівняння міркувань та фантазії. Навіть C викинув (або оголосив незаконними) певні типи псевдонімів та іншу поведінку, оскільки хотів більше оптимізації компілятора.

Тож питання: чого ти хочеш більше, ніж спадщина?


1
Тільки те, що явне управління пам’яттю та безпека типу абсолютно не пов’язані між собою та точно не несумісні. Те саме стосується мутації та "фантазійних типів". Я згоден із загальними міркуваннями, але ваші приклади обрані досить погано.
Конрад Рудольф

@KonradRudolph, управління пам'яттю та безпека типу тісно пов'язані. А free/ deleteоперація дає вам змогу визнати недійсними посилання; якщо ваша система типу не зможе відслідковувати всі посилання, які стосуються цього, це робить мову небезпечною. Зокрема, ні C, ні C ++ не є безпечними для типу. Це правда, що ви можете робити компроміси в тому чи іншому (наприклад, лінійні типи або обмеження розподілу), щоб змусити їх погодитися. Якщо бути точним, я мав би сказати, що Java хотіла безпеки типу із певною, простою системою типу та необмеженим розподілом більше.
Райан Калпепер

Добре, що залежить від того, як ви визначаєте безпеку типу. Наприклад, якщо ви приймаєте (цілком розумне) визначення типу типу компіляції і хочете, щоб цілісність посилань була частиною вашої системи типів, Java також не є безпечним для типу (оскільки це дозволяє nullпосилання). Заборона freeє досить довільним додатковим обмеженням. Підсумовуючи те, чи безпечна мова, залежить більше від вашого визначення безпеки типу, ніж від мови.
Конрад Рудольф

1
@Ryan: Покажчики ідеально безпечні для типу. Вони завжди поводяться відповідно до їх визначення. Це може бути не так, як ви їм подобаєтеся, але це завжди відповідно до того, як вони визначені. Ви намагаєтесь розтягнути їх, щоб вони були чимось, чого вони не є. Інтелектуальні покажчики можуть гарантувати безпеку пам’яті досить тривіально в C ++.
DeadMG

@KonradRudolph: у Java кожна Tпосилання стосується nullабо об'єкта класу, що розширюється T; nullнекрасиво, але операції з nullвикидання чітко визначеного винятку, а не пошкодження інваріанта типу вище. Контраст з C ++: після виклику delete, T*покажчик може вказувати на пам'ять , яка більше не утримує Tоб'єкт. Гірше, виконуючи завдання поля, використовуючи цей покажчик у призначенні, ви можете повністю оновити поле об'єкта іншого класу, якщо воно просто трапилось за сусідньою адресою. Це не типова безпека жодним корисним визначенням цього терміна.
Райан Калпеппер

1

Ні.

Якщо ви хочете видалити функцію основної мови, то ви загалом заявляєте, що це ніколи, ніколи не потрібно (або невиправдано важко реалізувати, що тут не застосовується). І "ніколи" - це сильне слово в інженерії програмного забезпечення. Вам потрібно дуже вагоме обґрунтування, щоб зробити таку заяву.


8
Це навряд чи вірно: весь дизайн Java стосувався видалення таких функцій, як перевантаження оператора, багаторазове успадкування та ручне управління пам’яттю. Крім того, я вважаю, що трактувати будь-які мовні особливості як "священну" неправильно; замість того, щоб виправдовувати видалення функції, слід обґрунтувати додавання її - я не можу придумати жодних функцій, які має мати кожна мова.
Тихон Єлвіс

6
@TikhonJelvis: Ось чому Java - це жахлива мова, і це не вистачає нічого, окрім "ВИКОРИСТУВАННЯ НАСЛІДЖЕНОГО ГАРБАЖУ" - причина номер один . Я погоджуюся, що мовні функції повинні бути виправданими, але ця функція є базовою мовою - у ній є багато корисних програм і програміст не може бути тиражований без порушення DRY.
DeadMG

1
@DeadMG вау! Досить сильна мова.
Крістофер

@DeadMG: Я почав писати спростування як коментар, але потім перетворив це на відповідь (qv).
Райан Калпеппер

1
"Досконалість досягається не тоді, коли не залишається нічого додати, але коли не залишається нічого для видалення" (q)
9000

1

Якщо говорити суворо з точки зору С ++, успадкування корисно для двох основних цілей:

  1. Це дозволяє повторно використовувати код
  2. У поєднанні з переосмисленням у дочірньому класі він дозволяє використовувати вказівник базового класу для обробки об’єкта дочірнього класу, не знаючи його типу.

Для пункту 1, якщо у вас є якийсь спосіб поділитися кодом без необхідності балуватися на занадто багато акробатики, ви можете усувати спадщину. Для пункту 2. Ви можете піти Java-шлях і наполягати на інтерфейсі, щоб реалізувати цю функцію.

Переваги зняття спадщини є

  1. запобігання тривалої ієрархії та пов'язаних з цим проблем. Ваш механізм спільного використання коду повинен бути досить хорошим для цього.
  2. Уникайте змін батьківських класів, коли вони не порушують дитячі класи.

Компроміс значною мірою між "Не повторюй себе" та Гнучкістю на одній стороні та уникненням проблем з іншого боку. Особисто мені б не хотілося бачити спадщину від C ++ просто, бо інший розробник може бути недостатньо розумним, щоб побачити проблеми.


1

Ви вважаєте, що добре не допускати спадкування чи Мікінс? (з огляду на те, що користувач хоч би мав інтерфейси)

Ви можете виконати дуже багато корисної роботи без успадкування впровадження або комбінацій, але мені цікаво, чи не маєте ви наслідувати якийсь інтерфейс, тобто декларацію, яка говорить про те, що якщо об’єкт реалізує інтерфейс A, то йому також потрібно інтерфейс B (тобто, А - це спеціалізація В і тому існує типове відношення). З іншого боку, вашому отриманому об'єкту потрібно лише записати, що він реалізує обидва інтерфейси, тому там не так вже й багато складності. Все ідеально виконано.

Відсутність успадкування реалізації має один очевидний недолік: ви не зможете побудувати числові індексовані vtables для своїх класів, і тому вам доведеться робити хеш-пошуки для кожного виклику методу (або з'ясувати розумний спосіб їх уникнути). Це може бути болісно, ​​якщо ви направляєте навіть основні значення (наприклад, числа) через цей механізм. Навіть дуже хороша хеш-реалізація може бути дорогою, коли ви натискаєте її кілька разів у кожному внутрішньому циклі!

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.