Эти два способа могут прекрасно жить вместе и фактически поддерживать друг друга.
Композиция просто играет модульно: вы создаете интерфейс, похожий на родительский класс, создаете новый объект и делегируете ему вызовы. Если эти объекты не должны знать друг о друге, это довольно безопасная и простая в использовании композиция. Здесь так много возможностей.
Однако, если родительский класс по какой-то причине нуждается в доступе к функциям, предоставляемым «дочерним классом» для неопытного программиста, может показаться, что это отличное место для использования наследования. Родительский класс может просто вызвать свой собственный абстрактный «foo ()», который перезаписывается подклассом, и затем он может дать значение абстрактной базе.
Это выглядит как хорошая идея, но во многих случаях лучше просто дать классу объект, который реализует foo () (или даже установить значение, предоставленное foo () вручную), чем наследовать новый класс от некоторой базы класс, для которого требуется указать функцию foo ().
Почему?
Поскольку наследование - плохой способ передачи информации .
Здесь у композиции есть реальное преимущество: связь может быть обратной: «родительский класс» или «абстрактный работник» могут агрегировать любые конкретные «дочерние» объекты, реализующие определенный интерфейс. тип родителя, который принимает его тип . И может быть любое количество объектов, например, MergeSort или QuickSort могут отсортировать любой список объектов, реализующих абстрактный интерфейс Compare. Или, говоря иначе: любая группа объектов, которые реализуют "foo ()", и другая группа объектов, которые могут использовать объекты, имеющие "foo ()", могут играть вместе.
Я могу вспомнить три реальные причины использования наследования:
- У вас есть много классов с одинаковым интерфейсом , и вы хотите сэкономить время на их написание
- Вы должны использовать один и тот же базовый класс для каждого объекта
- Вам нужно изменить приватные переменные, которые ни в коем случае не могут быть публичными
Если это правда, то, вероятно, необходимо использовать наследование.
Нет ничего плохого в использовании причины 1, очень хорошо иметь надежный интерфейс для ваших объектов. Это можно сделать с помощью композиции или с наследованием, нет проблем - если этот интерфейс прост и не меняется. Обычно наследование здесь достаточно эффективно.
Если причина номер 2, это немного сложно. Вам действительно нужно использовать только один и тот же базовый класс? В общем, просто использование одного и того же базового класса недостаточно, но это может быть требованием вашей среды, чего нельзя избежать при проектировании.
Однако, если вы хотите использовать закрытые переменные, случай 3, тогда у вас могут быть проблемы. Если вы считаете глобальные переменные небезопасными, то вам следует рассмотреть возможность использования наследования для получения доступа к закрытым переменным, также небезопасным . Имейте в виду, глобальные переменные - это еще не все, что Плохо - базы данных представляют собой большой набор глобальных переменных Но если вы можете справиться с этим, то это вполне нормально.