Возьмите в голову первый вопрос «Шаблоны проектирования» по Утиному симулятору - PullRequest
0 голосов
/ 08 января 2020

У меня проблемы с согласованием примера прохождения во вступлении к главе «Шаблоны проектирования». Я скопировал несколько изображений утверждения, решения и очевидных противоречий главы.

Проблема: Джо ругал за то, что на его симуляторе летали резиновые утки. В конце концов он предлагает "Как насчет интерфейса?" Там MallordDuck (и другие) объявляются как дети класса Duck и реализуют один или несколько интерфейсов.

Joe's proposal to use an Interface

За что его снова ругают леди-супервайзер, который спрашивает: «Как вы будете себя чувствовать, когда вам нужно изменить поведение летного на всех 48 уток?» В тексте справа от супервизора утверждается, что он полностью уничтожает повторное использование кода для такого поведения

Destroys code reuse

А в программировании для решения интерфейса UML-иллюстрация кажется, требует того же количества дубликата кода, за который Джо ругал. Кажется, это не решает эту проблему. Единственный способ, которым я могу придумать, - это поместить в интерфейс наиболее используемые реализации для fly () и Quack () (что невозможно в Java - я думаю), и это может быть чем-то, что вы не делаете хочу сделать, даже если бы это было возможно. Кроме того, кажется, что они также скрыты за интерфейсом ...

UML diagram illustrating programming to an interface

Ну, я либо слишком много обдумал, либо просто не Не понимаю утверждений о дублировании кода и «эти поведения больше не скрыты в наших классах Duck»

Любые разъяснения или дальнейшие объяснения очень приветствуются.

спасибо, Киал

Ответы [ 3 ]

1 голос
/ 09 января 2020

Шаблон, который вы описали в приведенном выше коде, основан на принципе разработки: «Инкапсулируйте то, что меняется».

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

Например, когда вы используете наследование, предположим, что у вас есть для создания n уток требуется n * 4 класса, скажем

Duck + fly + quack
Duck + not fly + not quack
Duck + fly + not quack
Duck + not fly + quack

Rubber duck - 4 classes like above
Mallard duck - 4 classes like above
Baby Duck - 4 classes like above.

For n ducks n*4 duck classes are required.

Вместо использования композиции или использования двух нижеследующих интерфейсов.

Flyable -> 2 classes fly and not fly 
Quackable -> 2 classes quack and not quack 
Duck -> 1 Duck class with above Flyable and Quackable.

With above structure, for n different type of ducks n+4 classes are required.
1 голос
/ 09 января 2020

Подход, принятый Head First, заключается в идентификации тех методов в суперклассе (в данном случае Duck), которые часто переопределяются его подклассами (RubberDuck, DecoyDuck). В вашем примере quack () и fly () часто являются переопределенными методами.

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

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

0 голосов
/ 09 января 2020

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

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

Ну, я либо слишком много думал об этом, либо я просто не понимаю утверждения о дублировании кода и "эти поведения больше не скрыты в наших классах Duck"

Они извлечены из реализаций классов Duck и помещены в новую иерархию Поведение классы. Затем классы Duck могут (по мере необходимости) использовать реализации этого поведения (подумайте о плагинах).

Основная идея c состоит в том, чтобы отдавать предпочтение композиции, а не наследованию, потому что композиция более гибкая для изменения и позволяет больше повторного использования. HAS-A может быть лучше, чем IS-A (см. Этот раздел).

enter image description here

Вместо каждого класса Duck, имеющего свой собственная реализация fly() или quack(), она собирается повторно использовать одно из 4 поведений (которые могут быть расширены позже, если потребуется), которые были инкапсулированы отдельно. Каждый Duck имеет поведение , опять же, как плагин, который он может использовать. При таком подходе вы можете даже уток изменить его поведение во время выполнения, переназначив его определение.

quackBehavior = /* choose a new implementation */

Есть еще одно (забавное) объяснение с JavaScript, но мне нравится идея хаотического изменения c, и как наследование может быть плохим решением: https://www.youtube.com/watch?v=wfMtDGfHWpA

...