"Віддавайте перевагу складу над спадщиною" - Є єдиною причиною захисту від змін підпису?


13

Ця сторінка виступає за склад над успадкуванням наступним аргументом (перефразовуючи це моїми словами):

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

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



1
див. також: Обговори це $ {blog}
gnat

2
Цитата не говорить, що це "єдина" причина.
Тулен Кордова

3
Склад майже завжди кращий, оскільки успадкування загалом додає складності, зв’язки, читання коду та зменшує гнучкість. Але є помітні винятки, іноді базовий клас або два не шкодять. Ось чому його « віддають перевагу », а не « завжди ».
Марк Роджерс

Відповіді:


27

Мені подобаються аналогії, ось ось одна: Ви коли-небудь бачили одне з тих телевізорів, на яких було вбудовано відеоплеєр? Як щодо тієї, що має відеомагнітофон та програвач DVD? Або той, що має Blu-ray, DVD та відеомагнітофон. О, і тепер всі трансляються, тому нам потрібно розробити новий телевізор ...

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

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


6
Було багато телевізорів з відеомагнітофонами та DVD-програвачами, вбудованими наприкінці 90-х та на початку 2000-х.
JAB

@JAB та багато (більшість?) Телевізорів, які сьогодні продаються, підтримують потокове передавання.
Кейсі

4
@JAB І жоден з них не може продати свій DVD-програвач, щоб допомогти придбати програвач Bluray
Олександр - Відновити Моніку

1
@JAB Правильно. Заява "Ви коли-небудь бачили одне з тих телевізорів, на яких був вбудований відеоплеєр?" означає, що вони існують.
JimmyJames

4
@Casey Тому що очевидно, ніколи не буде іншої технології, яка витіснить цю, чи не так? Я буду закладатися на те, що будь-який із цих телевізорів також підтримує входи із зовнішніх пристроїв на випадок, якщо хтось захоче користуватися чимось іншим, не купуючи новий телевізор. А точніше, мало освічених покупців готові придбати телевізор, якому бракує таких входів. Моя передумова не в тому, що таких підходів не існує, і я б не стверджував, що спадщина не існує. Справа в тому, що такі підходи за своєю суттю менш гнучкі, ніж композиція.
JimmyJames

4

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

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

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


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

1
@Brilliand Якби ви тільки знали, скільки написаний код java у стилі, як ви описуєте, і наскільки це жах ... Smalltalk ввів термін OOP, і він був розроблений навколо об'єктів, що отримують повідомлення. : "Обчислення слід розглядати як властивість об'єктів, до яких можна рівномірно викликати, надсилаючи повідомлення." Немає згадки про дані та функції, що працюють на них. Вибачте, але, на мою думку, ви сильно помиляєтесь. Якби мова йшла лише про дані та функції, можна було б із задоволенням продовжувати писати структури.
марстато

@Tsar, на жаль, навіть якщо Java підтримує статичні методи, культура Java не робить
k_g

@Brilliand Кому цікаво, що таке "OOP"? Важливе значення полягає в тому, як ви можете ним користуватися та результатами їх використання тими способами.
користувач253751

1
Після обговорення з @Tsar: Класи Java не повинні бути ідентифікованими, і дійсно такі класи, як UserRegistrationService і EmailService, як правило, не повинні бути ініціалізованими - класи без пов’язаних даних найкраще працюють як статичні класи (і як такі не мають відношення до оригіналу питання). Я буду видаляти деякі свої попередні коментарі, але моя оригінальна критика відповіді марсато стоїть.
Brilliand

4

"Віддавайте перевагу складу над спадщиною" - це просто хороша евристика

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

Я сподіваюсь, що ця точка з’ясується на цій посаді.

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


Приклад транспортного засобу

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

Ходімо варіант класичних прикладів , що деякі курси ООП використовувати ... У нас є Vehicleклас, то ми отримаємо Car, Airplane, Balloonі Ship.

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

Тоді Carі Airplaneможе бути якийсь загальний код, оскільки вони обидва можуть кататись на колесі по суші. Розробники можуть розглянути можливість створення посередницького класу в ланцюжку спадкування для цього. Тим не менш, насправді є і деякий загальний код між Airplaneі Balloon. Вони могли б розглянути можливість створення іншого посередницького класу в ланцюжку спадкування для цього.

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

Краще моделювати цю поведінку як інтерфейси та композицію, щоб ми могли використовувати її повторно, не впадаючи у спадщину кількох класів. Якщо розробники, наприклад, створюють FlyingVehiculeклас. Вони б сказали, що Airplaneце FlyingVehicule(спадкування класу), але ми могли б замість цього сказати, що Airplaneмає Flyingкомпонент (склад) і Airplaneє IFlyingVehicule(інтерфейс успадкування).

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

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

Все це без згадки Amphibious.

Насправді нам можуть не знадобитися речі, які сходять із землі. Стівен Гарн має красномовніший приклад у своїх статтях «Композиція користі над спадщиною», частина 1 та частина 2 .


Заміна та інкапсуляція

Чи слід Aуспадковувати чи складати B?

Якщо Aспеціалізація, Bяка повинна відповідати принципу заміщення Ліскова , успадкування є життєздатним, навіть бажаним. Якщо є ситуації, коли Aце не є дійсною заміною, Bми не повинні використовувати спадщину.

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

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

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

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

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

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

- Шаблони дизайну: елементи багаторазового об’єктно-орієнтованого програмного забезпечення, банда з чотирьох

Ну, це погано розроблений батьківський клас. Ось чому ви повинні:

Розробити на спадщину або заборонити це.

- Ефективна Java, Джош Блок


Проблема Йо-Йо

Ще один випадок, коли композиція допомагає - це проблема Йо-Йо . Це цитата з Вікіпедії:

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

Ви можете вирішити, наприклад: Ваш клас Cне успадкує від класу B. Натомість у вашого класу Cбуде член типу A, який може бути, а може і не бути (або мати) його об'єктом B. Таким чином, ви не будете програмувати не на деталі реалізації B, а на контракт, який пропонує інтерфейс A.


Лічильні приклади

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

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

Додаток : Є деякі інші способи, які можна дозволити розширювати ORM-об'єкти. Таким чином, я не думаю, що спадкування в цьому випадку не потрібне. Це дешевше.

Для іншого прикладу, двигун відеоігор може створити базовий клас, який використовує нативний код залежно від цільової платформи для виконання 3D-рендерінгу та обробки подій. Цей код складний і залежить від платформи. Користувачеві двигуна-розробника двигуна було б дорого та схильно до помилок мати справу з цим кодом, адже це є частиною причини використання двигуна.

Крім того, без частини 3D-рендерінга, саме стільки функціонують рамки віджетів. Це звільняє вас від занепокоєння щодо обробки повідомлень ОС… насправді, багатьма мовами ви не можете написати такий код без певної форми власних ставок. Більше того, якби ви це зробили, це обмежує вашу портативність. Натомість, з успадкуванням, за умови, що розробник не порушує сумісність (занадто багато); ви зможете легко перенести свій код на будь-які нові платформи, які вони підтримують у майбутньому.

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

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

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


Заключні думки

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

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

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


Гм .... Net Framework використовує кілька типів успадкованих потоків, щоб виконати роботу, пов'язану з потоком. Він працює надзвичайно добре, і це дуже просто у використанні та розширенні. Ваш приклад не дуже хороший ...
Т. Сар

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

Читання та запис структури з потоку краще виконувати з вільними функціями або мультиметодами.
користувач253751

@ jpmc26 Вибачте, ваш досвід був не дуже хорошим. Однак у мене не було жодних проблем із використанням або реалізацією матеріалів, пов’язаних із потоком, для різних речей.
Т. Сар

3

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

Приклад у wikipedia дає кращий приклад сили його підходу:

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


Тож цей розділ дає приклад складу, правда? Я прошу, бо це в статті не очевидно.
Утку

1
Так, "Склад над успадкуванням" не означає, що у вас немає інтерфейсів. Якщо ви подивитесь на об'єкт Player, ви зможете побачити, що він добре вписується в ієрархію, але код (вибачте за бруталілті) не походить від успадкування, але з композиції з деяким класом, який виконує твір
lbenini

2

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

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

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

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

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

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

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

Непряма дуже потужна. Використовуйте його розумно.


0

Ось ще одна причина: у багатьох мовах OO (я маю на увазі такі, як C ++, C # або Java), немає ніякого способу змінити клас об’єкта, коли ви його створили.

Припустимо, ми створюємо базу даних працівника. У нас є абстрактний базовий клас «Співробітник», і ми починаємо отримувати нові класи для конкретних ролей - інженер, охоронець, водій вантажівки тощо. Кожен клас має спеціалізовану поведінку на цю роль. Все добре.

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

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

Це набагато полегшує життя для створення конкретного класу «Співробітник» і розглядає його як контейнер, до якого ви можете додати властивості, наприклад, роль роботи.


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

0

Спадщина передбачає дві речі:

1) повторне використання коду: може бути легко виконано за допомогою композиції. Насправді це краще досягається за допомогою композиції, оскільки воно краще зберігає інкапсуляцію.

2) Поліморфізм: може здійснюватися через "інтерфейси" (класи, де всі методи чисто-віртуальні).

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

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

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


0

Це насправді єдина причина віддати перевагу композиції над спадщиною?

Ні.

Існує багато причин надати перевагу композиції над спадщиною. Немає єдиної правильної відповіді. Але я кину ще одну причину на користь композиції над спадщиною в суміш:

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

Ось як я думаю про це: коли я читаю чужий код, якщо я бачу, що вони розширили клас, я запитую, яку поведінку в суперкласі вони змінюють?

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

Ось конкретний приклад: у Java JFrameклас представляє вікно. Щоб створити вікно, ви створите примірник, JFrameа потім викликаєте функції цього примірника, щоб додати до нього речі та показати його.

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

З іншого боку, одним із способів виконання власного малювання є розширення JPanelкласу та перебір paintComponent()функції. Це прекрасне використання спадщини. Якщо я переглядаю ваш код і побачу, що ви розширили JPanelта переотримали paintComponent()функцію, я точно буду знати, яку поведінку ви змінюєте.

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


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

1
@ jpmc26 ... навіщо на землі вам потрібен заводський клас просто для створення нового примірника класу? І як успадкування покращує читабельність того, що ви описуєте?
Кевін Уордман

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

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

1
Не бачачи конкретного прикладу, важко реально коментувати. Але те, що ви описали, здається мені випадком надмірної інженерії. Потрібен примірник класу? newКлючове слово дає один. Все одно дякую за дискусію.
Кевін Уордман
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.