Дело не в том, что лучше, а в том, когда что использовать.
В «нормальных» случаях достаточно простого вопроса, чтобы выяснить, нужно ли нам наследование или агрегацию.
- Если новый класс является более или менее оригинальным классом. Используйте наследство. Новый класс теперь является подклассом исходного класса.
- Если новый класс должен иметь исходный класс. Используйте агрегацию. Новый класс теперь имеет оригинальный класс в качестве члена.
Тем не менее, есть большая серая зона. Итак, нам нужно несколько других трюков.
- Если мы использовали наследование (или мы планируем использовать его), но мы используем только часть интерфейса, или мы вынуждены переопределить множество функций, чтобы сохранить корреляцию логической. Тогда у нас большой неприятный запах, который указывает на то, что нам пришлось использовать агрегацию.
- Если мы использовали агрегацию (или планируем ее использовать), но мы обнаруживаем, что нам нужно скопировать почти все функциональные возможности. Тогда у нас есть запах, который указывает на наследство.
Короче говоря. Мы должны использовать агрегацию, если часть интерфейса не используется или должна быть изменена, чтобы избежать нелогичной ситуации. Нам нужно только использовать наследование, если нам нужны почти все функциональные возможности без серьезных изменений. И если есть сомнения, используйте агрегацию.
Другая возможность для случая, когда у нас есть класс, которому требуется часть функциональности исходного класса, - это разделить исходный класс на корневой класс и подкласс. И пусть новый класс наследуется от корневого класса. Но вы должны позаботиться об этом, чтобы не создавать нелогичное разделение.
Давайте добавим пример. У нас есть класс «Собака» с методами: «Ешь», «Прогулка», «Кора», «Играть».
class Dog
Eat;
Walk;
Bark;
Play;
end;
Теперь нам нужен класс «Cat», который нуждается в «Eat», «Walk», «Purr» и «Play». Итак, сначала попробуйте расширить его от собаки.
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;
Это намного чище. Нет внутренних собак. И кошки и собаки находятся на одном уровне. Мы можем даже представить других домашних животных, чтобы расширить модель. Если это не рыба или что-то, что не ходит. В этом случае нам снова нужно провести рефакторинг. Но это что-то другое время.