Композиция по наследству, но

13

Я пытаюсь научиться программировать и сталкиваюсь с противоречивой информацией, которая сбивает меня с толку.

Я изучал ООП и что такое абстрактные классы / интерфейсы и как их использовать, но потом я читаю, что нужно «отдавать предпочтение композиции, а не наследованию». Я понимаю, что композиция - это когда один класс создает / создает объект другого класса для использования / взаимодействия с функциональностью этого нового объекта.

Итак, мой вопрос ... я должен не использовать абстрактные классы и интерфейсы? Чтобы не создавать абстрактный класс и расширять / наследовать функциональность этого абстрактного класса в конкретных классах, а вместо этого просто создавать новые объекты, чтобы использовать функциональность другого класса?

Или я должен использовать композицию И наследовать от абстрактных классов; используя их обоих вместе? Если да, не могли бы вы привести примеры того, как это будет работать и каковы его преимущества?

Поскольку я больше всего чувствую себя комфортно с PHP, я использую его для улучшения своих навыков OOP / SE, прежде чем переходить на другие языки и переносить свои недавно приобретенные навыки SE, поэтому примеры с использованием PHP будут наиболее цениться.

MikeMason
источник
2
«Фавориты над наследством» - это глупая идея, которая имеет такой же смысл, как и «фавориты над сверлами». Это два разных инструмента, которые целесообразно использовать в двух разных случаях, и времена, когда вы действительно можете использовать один из них взаимозаменяемо, на самом деле довольно редки.
Мейсон Уилер
2
«Благосклонность X над Y» не означает, что никогда не используйте Y, просто подумайте, будет ли X лучше
Ричард Тингл
2
«Состав предпочтения по наследованию» более точно выражен как «Никогда, никогда, никогда не используйте наследование классов», с пояснением, что реализация интерфейса не является наследованием, и если выбранный вами язык поддерживает только абстрактные классы, а не интерфейсы, наследуя от 100% абстрактный класс также можно рассматривать как «не наследование».
Дэвид Арно
1
@MasonWheeler, нет действительных вариантов использования для наследования. Это, по крайней мере, такая же «злая» особенность, как «goto».
Дэвид Арно
1
@DavidArno Это просто смешно. Наследование является одной из самых полезных и продуктивных функций, когда-либо разработанных за всю историю программирования. Как и в случае с чем-либо полезным, существует множество способов злоупотреблять этим, но при правильном использовании это значительно увеличивает мощность и производительность вашей работы.
Мейсон Уилер

Ответы:

19

Композиция над наследованием означает, что когда вы хотите повторно использовать или расширять функциональность существующего класса, часто более целесообразно создать другой класс, который будет «оборачивать» существующий класс и использовать его реализацию внутри страны. Образец декоратора является примером этого.

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

Шаблонный метод является хорошим примером случая, когда наследование уместно.

Посмотрите этот ответ, чтобы узнать, как выбирать между составом и наследованием.

astreltsov
источник
+1 за указание на то, что в случае частичного повторного использования класса неиспользованная часть более аккуратно игнорируется при составлении, чем при наследовании.
Лоуренс
4

Я не достаточно знаком с PHP, чтобы дать вам конкретные примеры на этом языке, но вот несколько рекомендаций, которые я нашел полезными.

Интерфейсы / черты полезны для определения поведения во многих классах, которые могут иметь очень мало общего.

Например, если вы работаете с API-интерфейсом JSON, вы можете определить интерфейс, который определяет два метода: «toJson» и «toStatusCode». Это позволит вам написать помощника, который может взять любой объект, который реализует этот интерфейс, и преобразовать его в Http-ответ.

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

Прямое наследование лучше всего рассматривать как то, что вы делаете, когда для этого есть веские причины, а не как то, что вы делаете, если нет веских причин не делать этого.

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

Композиция должна быть вашей стратегией по умолчанию. Насколько это возможно, создайте интерфейсы и передайте их в качестве аргументов конструктора. Это сделает вашу жизнь намного проще при написании тестов.

Старайтесь как можно больше работать с глобальными синглетами, так как сравнение ожидаемого и фактического выходных данных является правильной задачей, если часть выходных данных зависит от состояния системных часов.

Это, конечно, не всегда возможно. Например, веб-платформа Play предоставляет библиотеку JSON, которая в основном является языком, специфичным для домена. Изменение этого было бы основным мероприятием, из которого замена вызовов на «Json.prettyPrint» была бы лишь очень незначительной частью.

Morgen
источник
1

Композиция - это когда класс предлагает некоторую функциональность путем создания экземпляра (возможно внутреннего) класса, который уже реализует эту функциональность, вместо того, чтобы наследовать от этого класса.

Так, например, если у вас есть класс, который моделирует корабль, и теперь вам говорят, что на вашем корабле должна быть вертолетная площадка, это не естественно, чтобы вывести ваш корабль из вертолетной площадки, (дух!) Вместо этого, вы должны иметь ваш корабль содержит класс вертолетной площадки и выставляет его с помощью некоторого Ship.getHelipad()метода.

В давние времена (лет десять назад) люди рассматривали наследование как быстрый и простой способ агрегирования функциональности, поэтому было много примеров типа «корабль наследуется от вертолетной площадки», которые были, конечно, очень неубедительными.

Но изречение «Композиция в пользу наследования» было тщательно сформулировано, чтобы дать понять, что это всего лишь предположение, а не правило. Автор изречения был достаточно осторожен, чтобы не говорить что-то вроде «ты никогда не будешь использовать наследство, только композицию». Это в основном привлекает внимание сообщества разработчиков программного обеспечения к тому факту, что наследование было чрезмерно использовано, в то время как во многих случаях композиция создает более четкие, элегантные и поддерживаемые проекты, чем наследование.

Таким образом, по сути, изречение «Композиция в пользу наследования» предполагает, что всякий раз, когда вы сталкиваетесь с «наследовать или сочинять?» вопрос, вы должны тщательно продумать, какая стратегия является наиболее подходящей, и что наиболее вероятно, что наиболее подходящей стратегией окажется композиция, а не наследование.

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

Вернемся к примеру с кораблем: если вашему кораблю нужно предложить интерфейс для взаимодействия с a FloatingMachine, то более естественно получить его из абстрактного FloatingMachineкласса, который, в свою очередь, вполне может быть получен из другого абстрактного Machineкласса.

Вот практическое правило для ответа на вопрос «композиция против наследования»:

Имеет ли мой класс отношение «есть» к интерфейсу, который он должен предоставить? Если да, используйте наследование. Если нет, используйте композицию.

Корабль "это" плавучая машина, а плавучая машина "это" машина. Таким образом, наследование идеально подходит для тех. Но корабль, конечно же, не вертолетная площадка. Так что лучше составьте функционал вертолетной площадки.

Майк Накис
источник