На прохання виходу, я розширюю свій коментар на більш довгу відповідь.
Найбільша помилка, яку роблять люди, вважає, що інтерфейси - це просто порожні абстрактні класи. Інтерфейс - це спосіб програміста сказати: "Мені все одно, що ти мені даєш, доки він дотримується цієї конвенції".
Бібліотека .NET є прекрасним прикладом. Наприклад, коли ви пишете функцію, яка приймає IEnumerable<T>
, те, що ви говорите, "Мені все одно, як ви зберігаєте свої дані. Я просто хочу знати, що я можу використовувати foreach
цикл.
Це призводить до отримання дуже гнучкого коду. Раптом для інтеграції все, що вам потрібно зробити, - це грати за правилами існуючих інтерфейсів. Якщо реалізація інтерфейсу є складною або заплутаною, то, можливо, це натяк на те, що ви намагаєтесь засунути квадратний кілочок у круглий отвір.
Але тоді виникає запитання: "А як щодо повторного використання коду? Мої професори CS сказали мені, що успадкування - це рішення всіх проблем повторного використання коду, і що спадщина дозволяє писати один раз і користуватися скрізь, і це вилікує менангіт у процесі порятунку сиріт з морів, що піднімаються, і більше не буде сліз, і далі, і т. д. і т. д. "
Використовувати спадщину лише тому, що вам подобається звучання слів «повторне використання коду» - досить погана ідея. Посібник із створення коду від Google робить це досить чітким:
Склад часто доречніший за спадщину. ... [B] оскільки код, що реалізує підклас, поширюється між базовим і підкласом, це може бути складніше зрозуміти реалізацію. Підклас не може змінювати функції, які не є віртуальними, тому підклас не може змінити реалізацію.
Щоб проілюструвати, чому успадкування не завжди є відповіддю, я буду використовувати клас під назвою MySpecialFileWriter †. Людина, яка сліпо вірить, що спадкування - це рішення всіх проблем, може стверджувати, що вам слід намагатися успадкувати FileStream
, щоб не дублювати FileStream
код. Розумні люди визнають, що це нерозумно. Вам слід просто мати FileStream
об’єкт у своєму класі (як локальну або змінну членів) і використовувати його функціональність.
FileStream
Приклад може здатися надуманим, але це не так . Якщо у вас є два класи, які реалізують один і той же інтерфейс абсолютно однаковим чином, тоді у вас повинен бути третій клас, який інкапсулює будь-яку операцію, що дублюється. Ваша мета повинна полягати в написанні класів, які є самостійними блоками для багаторазового використання, які можна скласти разом, як легоси.
Це не означає, що слід уникати успадкування будь-якою ціною. Є багато питань, які слід врахувати, і більшість з них буде охоплена дослідженням питання "Склад проти спадкування". Наш власний переповнення стека має кілька хороших відповідей на цю тему.
Зрештою, настрої вашого колеги не мають глибини та розуміння, необхідних для прийняття обґрунтованого рішення. Дослідіть тему і розгадайте самі.
† Ілюструючи спадщину, всі використовують тварин. Це марно. За 11 років розвитку я ніколи не писав клас на ім'я Cat
, тому не збираюся використовувати його як приклад.
alligator
eat
Звичайно, реалізація відрізняється тим, що вона приймаєcat
іdog
як параметри.