Композиция - это когда класс предлагает некоторую функциональность путем создания экземпляра (возможно внутреннего) класса, который уже реализует эту функциональность, вместо того, чтобы наследовать от этого класса.
Так, например, если у вас есть класс, который моделирует корабль, и теперь вам говорят, что на вашем корабле должна быть вертолетная площадка, это не естественно, чтобы вывести ваш корабль из вертолетной площадки, (дух!) Вместо этого, вы должны иметь ваш корабль содержит класс вертолетной площадки и выставляет его с помощью некоторого Ship.getHelipad()
метода.
В давние времена (лет десять назад) люди рассматривали наследование как быстрый и простой способ агрегирования функциональности, поэтому было много примеров типа «корабль наследуется от вертолетной площадки», которые были, конечно, очень неубедительными.
Но изречение «Композиция в пользу наследования» было тщательно сформулировано, чтобы дать понять, что это всего лишь предположение, а не правило. Автор изречения был достаточно осторожен, чтобы не говорить что-то вроде «ты никогда не будешь использовать наследство, только композицию». Это в основном привлекает внимание сообщества разработчиков программного обеспечения к тому факту, что наследование было чрезмерно использовано, в то время как во многих случаях композиция создает более четкие, элегантные и поддерживаемые проекты, чем наследование.
Таким образом, по сути, изречение «Композиция в пользу наследования» предполагает, что всякий раз, когда вы сталкиваетесь с «наследовать или сочинять?» вопрос, вы должны тщательно продумать, какая стратегия является наиболее подходящей, и что наиболее вероятно, что наиболее подходящей стратегией окажется композиция, а не наследование.
Но поскольку это не правило, вы также должны помнить, что во многих случаях наследование является более естественным. Если вы используете композицию там, где вы должны были использовать наследование, много зла постигнет ваш код.
Вернемся к примеру с кораблем: если вашему кораблю нужно предложить интерфейс для взаимодействия с a FloatingMachine
, то более естественно получить его из абстрактного FloatingMachine
класса, который, в свою очередь, вполне может быть получен из другого абстрактного Machine
класса.
Вот практическое правило для ответа на вопрос «композиция против наследования»:
Имеет ли мой класс отношение «есть» к интерфейсу, который он должен предоставить? Если да, используйте наследование. Если нет, используйте композицию.
Корабль "это" плавучая машина, а плавучая машина "это" машина. Таким образом, наследование идеально подходит для тех. Но корабль, конечно же, не вертолетная площадка. Так что лучше составьте функционал вертолетной площадки.