Чи можемо ми повністю замінити успадкування, використовуючи схему стратегії та введення залежності?


10

Наприклад:

var duckBehaviors = new Duckbehavior();
duckBehaviors.quackBehavior = new Quack();
duckBehaviors.flyBehavior = new FlyWithWings();
Duck mallardDuck = new Duck(DuckTypes.MallardDuck, duckBehaviors)

Оскільки клас Дак містить усі форми поведінки (реферат), створюється новий клас MallardDuck(який розширюється Duck), здається, не потрібно.

Довідка: Шаблон першого дизайну голови, глава 1.


Які типи Duckbehavior.quackBehaviorта інші поля 'у вашому коді?
max630

У вашому прикладі немає введення залежності.
Девід Конрад

11
Спадщина є дивовижною, коли ваш розробник молодшого та середнього рівня, оскільки навколо неї довга історія хитрощів та реструктуризації. Але більшість досвідчених розробників, з якими я говорив, віддають перевагу справді мілкої ієрархії успадкування на основі складу, коли це можливо. Спадкування може спричинити більш щільне з'єднання, ніж необхідно, і може ускладнити зміни.
Марк Роджерс


5
@DavidConrad: ОП не проводить нововведення в класі . DI / IoC - це про введення залежностей, а не про контейнери або про те, щоб ніколи не використовувати "нові"; це цілком ортогональне питання. Крім того, вам завжди доведеться десь змінити код - будь то корінь композиції або якийсь конфігураційний файл. Управління тут перевернуто в межах типу Дак, оскільки те, що контролює створення залежностей, - це не катор Дак, а деякий зовнішній контекст; це, як приклад іграшки, наведений для наочності, цілком чудово, що зовнішній контекст представлений лише кодом виклику.
Філіп Мілованович

Відповіді:


21

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

Шаблон стратегії дозволяє змінювати поведінку під час виконання одного інтерфейсу. Я міг сказати качки крижаниці літати і спостерігати, як вона літає крилами. Потім поміняйте його на качку пілота реактивного літака і спостерігайте, як він літає з авіакомпаніями Delta. Це під час роботи програми - це стратегічний зразок.

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

Якщо я маю конкретний клас качок, я можу його реалізувати, це поведінка мух. Я міг би навіть переконати, що він переводить поведінку з крила на крило на літо з дельтою на основі змінної стану. Ця змінна може бути булевою, int, або може бути a, FlyBehaviorяка має flyметод, який виконує будь-який стиль польоту, без того, щоб я мав тестувати це з if. Тепер я можу змінювати стилі польоту, не змінюючи типів качок. Тепер крижанки можуть стати пілотами. Це склад та делегування . Качка складається з FlyBehavior і вона може делегувати їй запити на політ. Ви можете замінити всі способи поведінки качки одразу таким чином або потримати щось за кожну поведінку чи будь-яку комбінацію між ними.

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

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

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

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

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

public class LoginFailure : System.ApplicationException {}

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

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


1
" Ніколи не занижуйте значення хорошого імені ". Новий час одягу імператора: LoginExceptionце не гарне ім’я. Це класичне "іменування смурфу", і якщо я забираю Exception, все, що я маю Login, це нічого не говорить про те, що пішло не так.
Девід Арно

На жаль, ми застрягли в глузливій конвенції Exceptionпро завершення кожного класу винятків, але, будь ласка, не плутайте "застрягли в ньому" з "хорошим".
Девід Арно

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

Я б назвав їх, StackOverflow, OutOfMemory, NullReferenceAccess, і LoginFailureт.д. У принципі, приймати «Exception» від імені. І, якщо потрібно, виправте їх так, щоб він описував, що пішло не так.
Девід Арно

@DavidArno за вашою командою.
candied_orange

7

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

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

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

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


-1

Спадщина не так важлива, як колись думалося. Це все ще важливо , і видалити його було б поганою помилкою.

Крайній приклад - Objective-C, де все є підкласом NSObject (компілятор насправді не дозволяє вам оголосити клас, який не має базового класу, тому все що не вбудовано в компілятор, повинно бути підкласом чогось вбудований у компілятор). У NSObject є багато корисних речей, які вам не доведеться пропускати.

Ще один цікавий приклад - UIView, базовий клас "view" в UIKit для розробки iOS. Це клас, який, з одного боку, трохи схожий на абстрактний клас, декларуючи функціональність, яку підкласи повинні заповнювати, але він також корисний і сам по собі. У ньому є підкласи, що постачаються UIKit, які часто використовуються як є. Є композиція розробником, який встановлює підгляди у вигляді. І часто існують визначені розробниками підкласи, які часто використовують склад. Не існує жодного суворого єдиного правила або навіть правил, ви використовуєте все, що найкраще відповідає вашим вимогам.


Ваш "крайній приклад" приблизно полягає в тому, як функціонують більшість сучасних мов OO: підкласи Python type, кожен ObjectObject
Delioth,

-1

[Я загрожу нахабною відповіддю на титульне запитання.]

Чи можемо ми повністю замінити успадкування, використовуючи схему стратегії та введення залежності?

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


2
" Шаблон стратегії працює на успадкування інтерфейсу ". Пам'ятайте, що стратегія - це модель дизайну; не схема реалізації. Таким чином, він може бути реалізований за допомогою інтерфейсів, але він однаково може бути реалізований, наприклад, за допомогою хеш-словника функцій.
Девід Арно

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

Плюс один, чувак: ... працює на успадкування інтерфейсу , кудо на належне використання загального терміна "інтерфейс". Я думаю, що поганий чи некомпетентний дизайн класу є основною причиною виникнення цього питання. Пояснити, чому / як би взяти книгу; чорт у деталях. Я працював над проектами спадкування, які були радістю побачити, і в той же час глибокою спадщиною, яка була пеклом. Я бачив краптастичний interfaceдизайн, закріплений за спадщиною. Тут прихована проблема полягає в непослідовному виконанні, що перетворює залежність поведінки. P, S. успадкування та інтерфейс не є взаємовиключними,
radarbob

@DavidArno коментар : Цей коментар буде добре, якщо він додається до питання про ОП. Питання щодо ОП технічно неправильно, але ми все ще отримуємо це. Питання не в цій конкретній відповіді.
radarbob

@DeepakMishra коментар: . "Інтерфейс" - це всі публічні члени класу. На жаль, значення "інтерфейсу" перевантажено. Ми повинні бути обережними, щоб відрізнити загальне значення за ключовим словом мови програмуванняinterface
radarbob

-2

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

Також я можу захотіти певної логіки навколо типів, щоб ієрархія типу могла бути кращою.

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

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

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

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