Звідки береться таке поняття «корисний склад над спадщиною»?


143

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

Також я ніколи не бачу реального пояснення того, чому успадкування погано, а склад хороший, що просто робить мене більш підозрілим. Чи варто це просто прийняти на віру? Заміна Ліскова та поліморфізм мають добре відомі, чіткі переваги, а ІМО складають всю точку використання об'єктно-орієнтованого програмування, і ніхто ніколи не пояснює, чому їх слід відмовитися від користі композиції.

Хтось знає, звідки походить це поняття, і яке обгрунтування воно стоїть?


45
Як зазначали інші, це вже давно - я здивований, що ви зараз це чуєте. Це інтуїтивно зрозуміло для тих, хто будує великі системи на таких мовах, як Java, протягом будь-якого часу. Це важливо для будь-якого інтерв'ю, яке я коли-небудь даю, і коли кандидат починає говорити про спадщину, я починаю сумніватися у їхньому рівні кваліфікації та кількості досвіду. Ось хороший вступ до того, чому спадкування є крихким рішенням (є багато інших): artima.com/lejava/articles/designprinciples4.html
січня

6
@Janx: Можливо, це все. Я не будую великих систем на таких мовах, як Java; Я будую їх у Delphi, і без заміни Ліскова та поліморфізму ми ніколи нічого не зробимо. Її об'єктна модель відрізняється певними способами, ніж у Java або C ++, і багато проблем, які, як видається, мають вирішити цю максиму, насправді не існують або є набагато менш проблемними в Delphi. Я думаю, різні точки зору з різних точок зору.
Мейсон Уілер

14
Я провів кілька років на командному будівництві відносно великих систем в Дельфах і високих деревах спадщини, безумовно, вкусив нашу команду і завдав нам значного болю. Я підозрюю, що ваша увага до принципів SOLID допомагає вам уникнути проблемних питань, а не використання Delphi.
Беван

8
Останні кілька місяців?!?
Жас

6
Ця концепція IMHO ніколи не була повністю адаптована до різноманітності мов, які підтримують як успадкування інтерфейсу (тобто підтипування чистих інтерфейсів), так і успадкування реалізації. Занадто багато людей дотримуються цієї мантри і не використовують достатньо інтерфейсів.
Урі

Відповіді:


136

Хоча я думаю, що я чув дискусії щодо складання проти успадкування задовго до GoF, я не можу покласти пальця на певне джерело. Можливо, Буч все одно.

<агент>

Але, як і багато мантр, і ця вироджувалася за типовими лініями:

  1. воно вводиться з детальним поясненням і аргументом шанованого джерела, який вигадує фразу як нагадування про первісну складну дискусію
  2. ним ділиться невідомий на деякий час відомий неміцний учасник клубу, як правило, коли коментують помилки n00b
  3. незабаром це повторюється бездумно тисячами тисяч, які ніколи не читають пояснення, але люблять використовувати це як привід не думати , а як дешевий і простий спосіб відчути себе вищим за інших
  4. врешті-решт, жодна кількість розумного розвінчання не може спричинити прилив «мему» - і парадигма перероджується в релігію та догму.

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

Склад і спадщина - це дуже різні речі, і їх не слід плутати один з одним. Хоча це правда, що композицію можна використовувати для імітації спадщини з великою кількістю зайвих робіт , але це не робить спадщину громадянином другого класу, а також не робить композицію улюбленим сином. Те, що багато n00bs намагаються використовувати спадщину як ярлик, не скасовує механізм, і майже всі n00bs навчаються на помилці і тим самим покращуються.

Подумайте, будь ласка, про свої дизайни та перестаньте розкидати гасла.

</rant>


16
Бух стверджує, що успадкування при впровадженні вводить високу взаємодію між класами. З іншого боку, він вважає успадкування ключовим поняттям, що відрізняє ООП від процесуального програмування.
Неманья Трифунович

13
Для такої речі має бути слово. Передчасна регургітація , можливо?
Йорг W Міттаг

8
@ Йорг: Ви знаєте, що станеться з таким терміном, як передчасна регургітація? Саме те, що пояснено вище. :) (Btw. Коли регургітація ніколи не передчасна?)
Bjarke Freund-Hansen

6
@Nemanja: Правильно. Питання в тому, чи справді ця зв’язка погана. Якщо класи концептуально сильно поєднані і концептуально утворюють взаємозв'язок супертип-підтип і ніколи не можуть бути концептуально роз'єднані, навіть якщо вони формально роз'єднані на мовному рівні, то міцне сполучення добре.
dimimcha

5
Мантра проста: "тільки тому, що ти можеш сформувати play-doh так, щоб нічого не означає, що ти повинен робити все з play-doh". ;)
Еван Плейс

73

Досвід.

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

Спадкування - це перш за все поліморфний інструмент, але деякі люди, на більшу частину своєї подальшої небезпеки, намагаються використовувати його як спосіб повторного використання / спільного використання коду. Обґрунтування: «добре, якщо я успадковую, то я отримую всі методи безкоштовно», але ігноруючи той факт, що ці два класи потенційно не мають поліморфного зв’язку.

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


7
Отже, ви не можете сказати, що "ви не повинні використовувати OOP в першу чергу, якщо ви не розумієте заміщення Ліскова", тому ви говорите "користь композиції над спадщиною", а не спроба обмежити шкоду, заподіяну некомпетентною кодери?
Мейсон Уілер

13
@ Мейсон: Як і будь-яка мантра, "користь композиції над успадкуванням" орієнтована на початківців програмістів. Якщо ви вже знаєте, коли використовувати спадщину і коли використовувати склад, то безглуздо повторювати мантру подібну.
Дін Гардінг

7
@Dean - Я не впевнений, що початківець такий же, як той, хто не розуміє кращих практик успадкування. Я думаю, що в цьому є більше, ніж у цьому. Погана спадщина є причиною багатьох, багатьох головних болів у моїй роботі, і це не код, написаний програмістами, які вважатимуться «початківцями».
Ніколь

6
@ Renesis: Перше, про що я думаю, коли чую, що «у деяких людей десять років досвіду, а у деяких людей один рік досвіду, повторений десять разів».
Мейсон Уілер

8
Я розробляв досить великий проект одразу після першого ОО "Отримання" і дуже покладався на нього. Хоча це добре працювало, я постійно вважав дизайн крихким - викликаючи незначні роздратування тут і там і іноді роблячи певні рефактори повною сукою. Це важко пояснити весь досвід, але словосполучення "Фаворит над спадщиною" описує це ТОЧНО. Зауважте, що це не говорить і навіть не означає "Уникайте спадкування", воно просто дає вам трохи натиснути, коли вибір не очевидний.
Білл К

43

Це не нова ідея, я вважаю, що вона насправді була внесена в книгу моделей GoF, що вийшла друком у 1994 році.

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

З книги GoF:

Спадкування розкриває підклас до деталей про реалізацію батьків, часто говорять, що "успадкування порушує інкапсуляцію"

Стаття з Вікіпедії до книги GoF має гідне вступ.


14
Я не згоден з цим. Вам не потрібно знати деталі реалізації класу, який ви успадковуєте; тільки громадськість та захищені члени, що піддаються впливу класу. Якщо вам доведеться знати деталі реалізації, то ви або хтось, хто написав базовий клас, чинили щось не так, і якщо недолік є в базовому класі, склад не допоможе вам виправити / обійти його.
Мейсон Уілер

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

7
@pholosodad: Я не погоджуюся з чимось, чого я не читав. Я не згоден з тим, що написав Дін, що "За визначенням ви повинні знати деталі реалізації класу, який ви успадковуєте", яке я прочитав.
Мейсон Уілер

10
Те, що я написав, - це лише короткий виклад того, що описано в книзі GoF. Я, можливо, я це сформулював трохи рішуче (вам не потрібно знати всі деталі реалізації), але це загальна причина, по якій Міністерство фінансів каже, що надає перевагу складу над спадщиною.
Дін Хардінг

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

26

Щоб відповісти на частину Вашого запитання, я вважаю, що ця концепція вперше з'явилася в Шаблоні дизайну GOF : Елементи багаторазового використання об'єктно-орієнтованого програмного забезпечення , яке було вперше опубліковано в 1994 році.

Вигідна композиція об'єкта над успадкуванням

Вони передмовляють це твердження коротким порівнянням спадщини із складом. Вони не кажуть "ніколи не використовуйте спадщину".


23

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

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

Це означає лише, що з спадщиною слід поводитися обережно, оскільки це коштує, а не те, що це не корисно. Насправді "Склад над спадщиною" часто закінчується "Склад + спадщина над спадщиною", оскільки ви часто хочете, щоб ваша складена залежність була абстрактним надкласом, а не конкретним підкласом. Це дозволяє перемикатися між різними конкретними реалізаціями вашої залежності під час виконання.

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

(Метафоричний) приклад може бути:

"У мене є клас Змія, і я хочу включити як частину цього класу те, що відбувається, коли Змія кусає. Мені б сподобалося, щоб Змія успадкувала клас BiterAnimal, який має метод Bite (), і замінить цей спосіб, щоб відобразити отруйний укус Але склад над успадкуванням попереджає мене про те, що я повинен намагатися використовувати композицію замість цього ... У моєму випадку це могло б перевести на змію, що має члена укусу. Клас укусу може бути абстрактним (або інтерфейсом) з кількома підкласами. Це дозволило б мені приємні речі, такі як підкласи VenomousBite та DryBite і можливість змінювати укуси на тому ж екземплярі Snake, що і змія зростає. Плюс обробка всіх ефектів укусу у своєму окремому класі може дозволити мені повторно використовувати його в тому класі Frost , тому що мороз кусає, але це не BiterAnimal і так далі ... "


4
Дуже хороший приклад зі Змією. З подібним випадком я нещодавно зустрічався з власними заняттями
Maksee

22

Деякі можливі аргументи для складу:

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

Композиція - це дуже простий і тактильний спосіб побудови предметів
Успадкування порівняно легко зрозуміти, але все ще не так легко продемонструвати в реальному житті. Багато предметів у реальному житті можна розбити на частини та скласти. Скажімо, велосипед можна побудувати за допомогою двох коліс, рами, сидіння, ланцюга тощо. Легко пояснюється складом. Тоді як у метафорі про спадщину ви можете сказати, що велосипед подовжує одноколісний велосипед, дещо здійсненний, але все ж набагато далі від реальної картини, ніж композиція (очевидно, це не дуже хороший приклад успадкування, але суть залишається тим самим). Навіть слово успадкування (принаймні, більшість американських носіїв англійської мови, якого я очікував) автоматично викликає значення у рядку "Щось передано від померлого родича", яке має певну кореляцію зі своїм значенням у програмному забезпеченні, але все ще лише слабко вписується.

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

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

Відмова від відповідальності:
Очевидно, це не ця чітка зрізана вулиця. Кожна конструкція заслуговує на оцінку декількох моделей / інструментів. Спадщина широко використовується, має безліч переваг і багато разів елегантніше, ніж композиція. Це лише деякі можливі причини, які можна використати, коли надавати перевагу композиції.


1
"Очевидно, це не цей чіткий зріз / вулиця в одну сторону". Коли склад не більш (або однаково) гнучкий, ніж спадкування? Я вважаю , що це дуже це одна вулиця з одностороннім рухом. Спадщина - це просто синтаксичний цукор для особливого випадку складу.
weberc2

Композиція часто не є гнучкою при використанні мови, яка реалізує композицію, використовуючи явно реалізовані інтерфейси, оскільки цим інтерфейсам не дозволяється еволюціонувати назад, сумісним з часом. #jmtcw
MikeSchinkel

11

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

Сенс концепції полягає в тому, що в спадщині є великі концептуальні витрати. Коли ви використовуєте успадкування, то кожен виклик методу має в ньому неявну відправку. Якщо у вас є глибокі дерева спадкування, або багаторазове відправлення, або (що ще гірше) обоє, то з'ясування, куди конкретний метод буде відправлений у будь-якому конкретному дзвінку, може стати королівським ПДФА. Це робить правильні міркування про код складнішими, а налагодження стає важче.

Дозвольте навести простий приклад для ілюстрації. Припустимо, що глибоко у дереві спадкування хтось назвав метод foo. Потім приходить хтось інший і додає fooна верхівці дерева, але робить щось інше. (Цей випак частіше зустрічається з багатократним успадкуванням.) Тепер ця людина, що працює в кореневому класі, зламала незрозумілий дочірній клас і, мабуть, не усвідомлює цього. Ви можете мати 100% покриття одиничними тестами і не помітити цю поломку, оскільки людина вгорі не подумає про тестування дитячого класу, а тести для дочірнього класу не думають перевіряти нові методи, створені вгорі . (Правда, є способи написання одиничних тестів, які це сприймуть, але є також випадки, коли ви не можете легко написати тести таким чином.)

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

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

На жаль, багато розробників дізнаються про об'єктно-орієнтоване програмне забезпечення, дізнаються про успадкування, а потім виходять, щоб якомога частіше використовувати свою нову сокиру. Що означає, що вони намагаються використовувати спадщину там, де композиція була правильним інструментом. Сподіваємось, вони вчасно навчаться краще, але часто це відбувається не після того, як кілька видалених кінцівок і т. Д. Повідомлення їм наперед, що це погана ідея, як правило, прискорює процес навчання і зменшує травми.


3
Гаразд, я думаю, це має сенс з точки зору C ++. Це те, про що я ніколи не замислювався, бо це не проблема в Delphi, чим я користуюся більшу частину часу. (Немає багаторазового успадкування; якщо у вас є метод у базовому класі та інший метод з тим самим іменем у похідному класі, який не переосмислює базовий метод, компілятор видасть попередження, тому ви не випадково закінчитеся з такою проблемою.)
Мейсон Уілер

1
@Mason: Версія Obnder Pascal (Андер) перевершує C ++ та Java, коли мова йде про крихку проблему успадкування базового класу. На відміну від C ++ та Java, перевантаження успадкованого віртуального методу не є неявною.
біт-твідлер

2
@ bit-twiddler, що ви говорите про C ++ та Java, можна сказати про Smalltalk, Perl, Python, Ruby, JavaScript, Common Lisp, Objective-C та будь-якій іншій мові, яку я вивчив, що забезпечує будь-яку форму підтримки OO. Однак, googling говорить про те, що C # слідує за лідером Object Pascal.
btilly

3
Це тому, що Андерс Хейлсберг створив аромат Borland на Object Pascal (він же Delphi) та C #.
біт-трейдлер

8

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

Спадкування від класу передбачає багато речей. Це означає, що Похідне - це тип Бази (див. Принцип заміщення Ліскова для деталей горі), оскільки в будь-якому випадку, коли Ви використовуєте Базу, було б сенсом використовувати Похідне. Це надає отриманому доступу до захищених членів та функцій членів Base. Це тісний взаємозв'язок, це означає, що він має високу зв’язок, а зміни в одній, швидше за все, вимагають змін до іншої.

Зчеплення - це погана річ. Це робить програми важче зрозуміти та змінити. За інших рівних завдань завжди слід вибирати варіант із меншим з'єднанням.

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


"За інших обставин успадкування працюватиме, а склад не буде". Коли? Спадкування можна змоделювати за допомогою поліморфізму композиції та інтерфейсу, тому я буду здивований, якщо є обставини, за яких композиція не буде працювати.
weberc2

8

Ось два мої центи (за всіма відмінними балами, вже піднятими):

ІМХО. Це зводиться до того, що більшість програмістів насправді не отримують спадщину, і, в кінцевому підсумку, перестараються, частково в результаті навчання цієї концепції. Ця концепція існує як спосіб спробувати відмовити їх перестаратися, а не зосередитись на тому, щоб навчити їх правильно робити.

Кожен, хто витратив час на викладання чи наставництво, знає, що саме це часто трапляється, особливо з новими розробниками, які мають досвід інших парадигм:

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

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

У 80% випадків це досить добре. Але інші 20% - там, де починається проблема. Для того, щоб уникнути перезапису та переконатися, що вони скористалися спільною реалізацією, вони починають розробляти свою ієрархію навколо передбачуваної реалізації, а не основні абстракції. "Стек успадковується з подвійно пов'язаного списку" є хорошим прикладом цього.

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

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


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

8

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

Я так сподіваюся.

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

У більшості мов OO при успадкуванні від базового класу ви:

  • успадковувати з його інтерфейсу
  • успадковувати від його реалізації (і дані, і методи)

І тут стають біди.

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

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

В якості контрагента Haskellдозволяють використовувати Принцип Ліскова лише тоді, коли "виходять" з чистих інтерфейсів (званих понять) (1). Ви не можете вивести з існуючого класу, лише композиція дозволяє вбудовувати його дані.

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


7

Проста відповідь: спадкування має більшу сполученість, ніж склад. З урахуванням двох варіантів, інакше еквівалентних якостей, виберіть той, який менш сполучений.


2
Але в цьому вся суть. Вони не "інакше еквівалентні". Спадщина дає можливість заміщення Ліскова та поліморфізму, який є сутністю використання OOP. Склад ні.
Мейсон Уілер

4
Поліморфізм / ЛСП - не "вся суть" ООП, це одна особливість. Існує також інкапсуляція, абстрагування тощо. Спадщина означає, що відносини "є", моделі узгодження "мають" відносини.
Стів

@Steve: Ви можете робити абстракції та інкапсуляцію в будь-якій парадигмі, яка підтримує створення структур даних та процедур / функцій. Але поліморфізм (конкретно мається на увазі віртуальний метод диспетчеризації в цьому контексті) та заміна Ліскова є унікальними для об'єктно-орієнтованого програмування. Це я мав на увазі під цим.
Мейсон Уілер

3
@ Мейсон: Настанова полягає в тому, щоб "віддавати перевагу складу над спадщиною". Це жодним чином не означає не використовувати і навіть не уникати спадкування. Все говорить про те, що коли всі інші речі рівні, вибирайте склад. Але якщо вам потрібна спадщина, використовуйте її!
Джефрі Фауст

7

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

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

Мається на увазі, що коли вам дійсно потрібне успадкування, це очевидно, і ви повинні використовувати його тоді.

Також відповідь Стівена Лоу. Дійсно, справді це.


1
Мені подобається ваша аналогія
barrylloyd

2

Спадщина не сама по собі погана, а композиція не сама по собі хороша. Вони є лише інструментами, які програміст ОО може використовувати для проектування програмного забезпечення.

Якщо ви дивитесь на клас, чи робить це більше, ніж абсолютно слід ( SRP )? Це дублювання функціональності без потреби ( DRY ), або він надто зацікавлений у властивостях або методах інших класів ( Feature Envy ) ?. Якщо клас порушує всі ці поняття (і, можливо, більше), чи намагається бути класом Бога . Це низка проблем, які можуть виникнути при розробці програмного забезпечення, жодна з яких не є обов'язковою проблемою успадкування, але це може швидко створити головні болі та крихкі залежності, де також застосований поліморфізм.

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

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


-1

Справжня цитата, я вважаю, походить з "Ефективної Java" Джошуа Блоха, де вона названа однією з глав. Він стверджує, що успадкування є безпечним у межах пакету та там, де клас був спеціально розроблений та документально підтверджений для продовження. Як стверджують інші, він стверджує, що успадкування порушує інкапсуляцію, оскільки підклас залежить від деталей реалізації його суперкласу. Це призводить, каже він, до крихкості.

Хоча він не згадує про це, мені здається, що одне успадкування на Java означає, що композиція дає вам набагато більше гнучкості, ніж успадкування.

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