Конечно, но мы называем этот состав и делегирование . Шаблон стратегии и внедрение зависимостей могут показаться структурно схожими, но их намерения разные.
Шаблон стратегии позволяет изменять поведение во время выполнения под тем же интерфейсом. Я мог бы сказать крякве летать и смотреть, как она летит с крыльями. Тогда обменяйте это на утку пилота реактивного самолета и наблюдайте, как это летит с авиакомпаниями Дельты. Делать это во время работы программы - это образец стратегии.
Внедрение зависимостей - это метод, позволяющий избежать жесткого кодирования зависимостей, чтобы они могли меняться независимо, не требуя изменения клиентов при их изменении. Клиенты просто выражают свои потребности, не зная, как их встретят. Таким образом, как они встречаются, решается в другом месте (как правило, в основном). Вам не нужны две утки, чтобы использовать эту технику. Просто то, что использует утка, не зная и не заботясь, какая утка. Что-то, что не строит утку и не ищет ее, но совершенно рад использовать любую утку, которую вы ей подадите.
Если у меня есть конкретный класс утка, я могу реализовать его поведение мухи. Я мог бы даже заставить его переключать поведение с полета с крыльями на полет с дельтой, основываясь на переменной состояния. Эта переменная может быть логическое значение, INT, или это может быть , FlyBehavior
что есть fly
метод , который делает все , что летающий стиль без меня, чтобы проверить его с если. Теперь я могу менять стиль полета, не меняя типы уток. Теперь кряквы могут стать летчиками. Это состав и делегирование . Утка состоит из поведения FlyBehavior и может передавать ему запросы на полет. Таким образом, вы можете заменить все свое поведение утки одновременно или оставить что-то для каждого поведения или любой комбинации между ними.
Это дает вам все те же полномочия, которыми обладает наследство, кроме одного. Наследование позволяет вам выразить, какие методы Duck вы переопределяете в подтипах Duck. Композиция и делегирование требуют, чтобы Утка явно делегировала подтипам с самого начала. Это гораздо более гибко, но требует больше ввода с клавиатуры, и Дак должен знать, что это происходит.
Однако многие люди считают, что наследование должно быть явно разработано с самого начала. И что, если это не так, вы должны пометить свои классы как запечатанные / окончательные, чтобы запретить наследование. Если вы придерживаетесь этого взгляда, то наследование действительно не имеет преимуществ перед составом и делегированием. Потому что тогда в любом случае вам нужно либо спроектировать для расширяемости с самого начала, либо быть готовым все разрушить позже.
Сносить вещи на самом деле является популярным вариантом. Просто знайте, что есть случаи, когда это проблема. Если вы самостоятельно развернули библиотеки или модули кода, которые вы не собираетесь обновлять в следующем выпуске, вы можете столкнуться с тем, что имеете дело с версиями классов, которые ничего не знают о том, что вы делаете.
Хотя готовность снести вещи позже может освободить вас от чрезмерного проектирования, есть что-то очень мощное в способности спроектировать то, что использует утку, без необходимости знать, что на самом деле будет делать утка при использовании. Это незнание - сильная вещь. Это позволяет вам на некоторое время перестать думать об утках и подумать об остальном коде.
«Можем ли мы» и «должны ли мы» - это разные вопросы. Композиция Favor Over Inheritance не говорит, что никогда не используйте наследование. Есть еще случаи, когда наследование имеет наибольшее значение. Я покажу вам мой любимый пример :
public class LoginFailure : System.ApplicationException {}
Наследование позволяет создавать исключения с более конкретными описательными именами в одной строке.
Попробуйте сделать это с композицией, и вы получите беспорядок. Кроме того, нет риска возникновения проблемы наследования, поскольку здесь нет данных или методов для повторного использования и поощрения цепочки наследования. Все это добавляет хорошее имя. Никогда не стоит недооценивать ценность доброго имени.
Duckbehavior.quackBehavior
и другие поля в вашем коде?