Например:
var duckBehaviors = new Duckbehavior();
duckBehaviors.quackBehavior = new Quack();
duckBehaviors.flyBehavior = new FlyWithWings();
Duck mallardDuck = new Duck(DuckTypes.MallardDuck, duckBehaviors)
Поскольку класс Duck содержит все типы поведения (абстрактные), создание нового класса MallardDuck
(который расширяется Duck
), по-видимому, не требуется.
Ссылка: Head First Design Pattern, глава 1.
design-patterns
dependency-injection
strategy-pattern
Дипак Мишра
источник
источник
Duckbehavior.quackBehavior
и другие поля в вашем коде?Ответы:
Конечно, но мы называем этот состав и делегирование . Шаблон стратегии и внедрение зависимостей могут показаться структурно схожими, но их намерения разные.
Шаблон стратегии позволяет изменять поведение во время выполнения под тем же интерфейсом. Я мог бы сказать крякве летать и смотреть, как она летит с крыльями. Тогда обменяйте это на утку пилота реактивного самолета и наблюдайте, как это летит с авиакомпаниями Дельты. Делать это во время работы программы - это образец стратегии.
Внедрение зависимостей - это метод, позволяющий избежать жесткого кодирования зависимостей, чтобы они могли меняться независимо, не требуя изменения клиентов при их изменении. Клиенты просто выражают свои потребности, не зная, как их встретят. Таким образом, как они встречаются, решается в другом месте (как правило, в основном). Вам не нужны две утки, чтобы использовать эту технику. Просто то, что использует утка, не зная и не заботясь, какая утка. Что-то, что не строит утку и не ищет ее, но совершенно рад использовать любую утку, которую вы ей подадите.
Если у меня есть конкретный класс утка, я могу реализовать его поведение мухи. Я мог бы даже заставить его переключать поведение с полета с крыльями на полет с дельтой, основываясь на переменной состояния. Эта переменная может быть логическое значение, INT, или это может быть ,
FlyBehavior
что естьfly
метод , который делает все , что летающий стиль без меня, чтобы проверить его с если. Теперь я могу менять стиль полета, не меняя типы уток. Теперь кряквы могут стать летчиками. Это состав и делегирование . Утка состоит из поведения FlyBehavior и может передавать ему запросы на полет. Таким образом, вы можете заменить все свое поведение утки одновременно или оставить что-то для каждого поведения или любой комбинации между ними.Это дает вам все те же полномочия, которыми обладает наследство, кроме одного. Наследование позволяет вам выразить, какие методы Duck вы переопределяете в подтипах Duck. Композиция и делегирование требуют, чтобы Утка явно делегировала подтипам с самого начала. Это гораздо более гибко, но требует больше ввода с клавиатуры, и Дак должен знать, что это происходит.
Однако многие люди считают, что наследование должно быть явно разработано с самого начала. И что, если это не так, вы должны пометить свои классы как запечатанные / окончательные, чтобы запретить наследование. Если вы придерживаетесь этого взгляда, то наследование действительно не имеет преимуществ перед составом и делегированием. Потому что тогда в любом случае вам нужно либо спроектировать для расширяемости с самого начала, либо быть готовым все разрушить позже.
Сносить вещи на самом деле является популярным вариантом. Просто знайте, что есть случаи, когда это проблема. Если вы самостоятельно развернули библиотеки или модули кода, которые вы не собираетесь обновлять в следующем выпуске, вы можете столкнуться с тем, что имеете дело с версиями классов, которые ничего не знают о том, что вы делаете.
Хотя готовность снести вещи позже может освободить вас от чрезмерного проектирования, есть что-то очень мощное в способности спроектировать то, что использует утку, без необходимости знать, что на самом деле будет делать утка при использовании. Это незнание - сильная вещь. Это позволяет вам на некоторое время перестать думать об утках и подумать об остальном коде.
«Можем ли мы» и «должны ли мы» - это разные вопросы. Композиция Favor Over Inheritance не говорит, что никогда не используйте наследование. Есть еще случаи, когда наследование имеет наибольшее значение. Я покажу вам мой любимый пример :
Наследование позволяет создавать исключения с более конкретными описательными именами в одной строке.
Попробуйте сделать это с композицией, и вы получите беспорядок. Кроме того, нет риска возникновения проблемы наследования, поскольку здесь нет данных или методов для повторного использования и поощрения цепочки наследования. Все это добавляет хорошее имя. Никогда не стоит недооценивать ценность доброго имени.
источник
LoginException
не хорошее имя. Это классическое «smurf naming», и, если я уберуException
, все, что у меня естьLogin
, это ничего не говорит мне о том, что пошло не так.Exception
об окончании каждого класса исключений, но, пожалуйста, не путайте «застрял с этим» с «хорошим».StackOverflow
,OutOfMemory
,NullReferenceAccess
, иLoginFailure
т.д. В принципе, принимать «Exception» от имени. И, если необходимо, исправьте их так, чтобы они описывали, что пошло не так.Вы можете заменить практически любую методологию любой другой методологией и при этом производить работающее программное обеспечение. Тем не менее, некоторые из них лучше подходят для конкретной проблемы, чем другие.
Это зависит от многих вещей, которые предпочтительнее. Уровень техники в применении, опыт работы в команде, ожидаемые будущие разработки, личные предпочтения и то, как трудно новичку разобраться в этом, и это лишь некоторые из них.
По мере того, как вы становитесь более опытным и чаще боретесь с творениями других людей, вы, скорее всего, будете уделять больше внимания последнему созерцанию.
Наследование по-прежнему является действительным и надежным инструментом моделирования, который не всегда является наиболее гибким, но он предлагает четкие рекомендации для новых людей, которые могут быть благодарны за четкое сопоставление проблемной области.
источник
Наследование не так важно, как когда-то думали. Это все еще важно , и удаление это было бы плохой ошибкой.
Крайним примером является Objective-C, где все является подклассом NSObject (компилятор фактически не позволяет вам объявить класс, у которого нет базового класса, поэтому все, что не встроено в компилятор, должно быть подклассом чего-то встроен в компилятор). В NSObject встроено множество полезных вещей, которые вам не придется упускать.
Другой интересный пример - UIView, базовый класс «view» в UIKit для разработки под iOS. Это класс, который с одной стороны немного похож на абстрактный класс, объявляющий функциональность, которую должны заполнять подклассы, но он также полезен сам по себе. У него есть подклассы, предоставляемые UIKit, которые часто используются как есть. Композиция, разработанная разработчиком, устанавливает подпредставления в виде. И есть часто определяемые разработчиком подклассы, которые часто используют композицию. Нет строгого единого правила или даже правил, вы используете то, что лучше всего соответствует вашим требованиям.
источник
type
, все не примитивы в Java наследуютсяObject
, у всего в JavaScript есть прототип, заканчивающийсяObject
и т. Д.[Я рискну нахальный ответ на главный вопрос.]
Да ... кроме того, что сам шаблон стратегии использует наследование. Шаблон стратегии работает на наследование интерфейса. Замена наследования на состав + стратегия перемещает наследование в другое место. Тем не менее, такая замена часто стоит того, потому что она позволяет разделить иерархии .
источник
interface
дизайн, исправленный по наследству. Скрытая проблема здесь заключается в несовместимом поведении, зависящем от изменения поведения. P, S. наследование и интерфейс не являются взаимоисключающими,interface
Нет. Если утке кряквы требуются другие параметры для создания экземпляра, чем у утки какого-либо другого типа, это было бы кошмаром для изменения. Также вы должны быть в состоянии создать утку? Это скорее утка кряква, утка мандаринка или все остальные виды уток.
Также мне может потребоваться некоторая логика вокруг типов, чтобы иерархия типов была лучше.
Теперь, если повторное использование кода становится проблематичным для некоторых функций, мы могли бы составить его, передав функции через конструктор.
Опять же, это действительно зависит от вашего варианта использования. Если у вас есть только утка и кряква, иерархия классов будет гораздо более простым решением.
Но вот пример, где вы хотели бы использовать шаблон стратегии: если у вас есть классы клиентов, и вы хотите передать стратегию биллинга (интерфейс), которая может быть стратегией биллинга «счастливый час» или обычной стратегией биллинга, вы можете передать ее в конструкторе. Таким образом, вам не нужно делать два разных класса клиентов, и вам не нужна иерархия. У вас просто один класс клиента.
источник