Справа не в тому, що найкраще, а в тому, коли використовувати.
У «нормальних» випадках достатньо простого питання, щоб з’ясувати, чи потрібно нам успадкування чи об’єднання.
- Якщо новий клас є більш-менш оригінальним класом. Використовуйте спадщину. Новий клас тепер є підкласом початкового класу.
- Якщо новий клас повинен мати початковий клас. Використовуйте агрегацію. У новому класі тепер є початковий клас як член.
Однак є велика сіра зона. Тому нам потрібно кілька інших хитрощів.
- Якщо ми використовували успадкування (або плануємо його використовувати), але ми використовуємо лише частину інтерфейсу, або ми змушені перекрити багато функціональності, щоб зберегти кореляцію логічною. Тоді у нас великий неприємний запах, який вказує на те, що нам довелося використовувати агрегацію.
- Якщо ми використовували агрегацію (або плануємо її використовувати), але виявляємо, що нам потрібно скопіювати майже всю функціональність. Тоді у нас є запах, який вказує у напрямку успадкування.
Щоб скоротити його. Ми повинні використовувати агрегацію, якщо частина інтерфейсу не використовується або має бути змінена, щоб уникнути нелогічної ситуації. Нам потрібно використовувати лише успадкування, якщо нам потрібна майже вся функціональність без великих змін. А коли сумніваєтесь, використовуйте Агрегацію.
Інша можливість для випадку, коли у нас є клас, який потребує частини функціональності початкового класу, - це розділити початковий клас на кореневий клас і підклас. І нехай новий клас успадкує від кореневого класу. Але вам слід подбати про це, а не створювати нелогічне розмежування.
Додамо приклад. У нас є клас "Собака" методами: "Їжте", "Гуляйте", "Гавкайте", "Грайте".
class Dog
Eat;
Walk;
Bark;
Play;
end;
Зараз нам потрібен клас «Кішка», якому потрібні «Їжте», «Прогулянка», «Мур» та «Грайте». Тому спочатку спробуйте поширити це від Собаки.
class Cat is Dog
Purr;
end;
Виглядає, гаразд, але чекай. Цей кіт може гавкати (любителі котів за це мене вб’ють). І гавкаючий кіт порушує принципи Всесвіту. Тому нам потрібно перекрити метод Барка, щоб він нічого не робив.
class Cat is Dog
Purr;
Bark = null;
end;
Гаразд, це працює, але пахне погано. Тож давайте спробуємо агрегацію:
class Cat
has Dog;
Eat = Dog.Eat;
Walk = Dog.Walk;
Play = Dog.Play;
Purr;
end;
Гаразд, це приємно. Ця кішка більше не гавкає, навіть не мовчить. Але все ж є внутрішня собака, яка хоче вийти. Тож давайте спробуємо рішення номер три:
class Pet
Eat;
Walk;
Play;
end;
class Dog is Pet
Bark;
end;
class Cat is Pet
Purr;
end;
Це набагато чистіше. Жодних внутрішніх собак. І коти, і собаки на одному рівні. Ми навіть можемо представити інших домашніх тварин для розширення моделі. Якщо тільки це не риба, чи щось, що не ходить. У такому випадку нам знову потрібно рефактор. Але це щось інше.