Стандарты программирования на С++. 101 правило и рекомендация | страница 58



• Монолитные классы сложнее сделать корректными и безопасными в связи с тем, что при их разработке зачастую нарушается принцип "Один объект — одна задача" (см. рекомендации 5 и 44).

Ссылки

[Cargill92] pp. 85-86, 152, 174-177 • [Lakos96] §0.2.1-2, §1.8, §8.1-2 • [Meyers97] §18 • [Stroustrup00] §16.2.2, §23.4.3.2, §24.4.3 • [Sutter04] §37-40

34. Предпочитайте композицию наследованию

Резюме

Избегайте "налога на наследство": наследование — вторая по силе после отношения дружбы взаимосвязь, которую можно выразить в С++. Сильные связи нежелательны, и их следует избегать везде, где только можно. Таким образом, следует предпочитать композицию наследованию, кроме случаев, когда вы точно знаете, что делаете и какие преимущества дает наследование в вашем проекте.

Обсуждение

Наследованием часто злоупотребляют даже опытные разработчики. Главное правило в разработке программного обеспечения — снижение связности. Если взаимоотношение можно выразить несколькими способами, используйте самую слабую из возможных взаимосвязей.

Известно, что наследование — практически самое сильное взаимоотношение, которое можно выразить средствами С++; сильнее его только отношение дружбы, и пользоваться им следует только при отсутствии функционально эквивалентной более слабой альтернативы. Если вы можете выразить отношения классов с использованием только лишь композиции, следует использовать этот способ.

В данном контексте "композиция" означает простое использование некоторого типа в виде переменной-члена в другом типе. В этом случае вы можете хранить и использовать объект таким образом, который обеспечивает вам контроль над степенью взаимосвязи.

Композиция имеет важные преимущества над наследованием.

• Большая гибкость без влияния на вызывающий код: закрытые члены-данные находятся под полным вашим контролем. Вы можете хранить их по значению, посредством (интеллектуального) указателя или с использованием идиомы Pimpl (см. рекомендацию 43), при этом переход от одного способа хранения к другому никак не влияет на код вызывающей функции: все, что при этом меняется, — это реализация функций-членов класса, использующих упомянутые члены-данные. Если вы решите, что вам требуется иная функциональность, вы можете легко изменить тип или способ хранения члена при полной сохранности открытого интерфейса. Если же вы начнете с открытого наследования, то скорее всего вы не сможете легко и просто изменить ваш базовый класс в случае необходимости (см. рекомендацию 37).