×

Проектирование паттернов

Привет, друзья!

По рекомендациям форумчан начал читать параллельно про паттерны. Вчера несколько раз перечитал страниц двадцать, не могу понять.
В книге даётся один из принципов проектирования, когда нужно выделить то, что изменяется, инкапсулировать, чтобы оно не влияло на работу остального кода.
Далее, приводиться картинка, на которой нужно отобразить (карандашом), отношение между классами:

s018.radikal.ru/...​/1202/c9/1af1d7f6cf1d.png

Мне стало не понятным, что значить инкапсулированы? Раньше, я думал, что инкапсуляция связана тесно с реализацией класса. Т.е. моя задача максимально защитить данные, скрыть реализацию от программиста-клиента и, оставить для него только интерфейс.

В классе Duck объявлены два члена класса, поля:

FlyBehavior flyBehavior;
QuackBehavior quackBehavior;

у которых модификатор доступа — в пределах пакета. Соответственно, они не инкапсулированы. В моём понимании инкапсулировать, если две переменные объявить с модификатором protected, чтобы они были доступны исключительно для потомков и скрыты от внешнего мира.

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

И второй вопрос, почему у потомков отношение классов только «является» судя по этой таблице, если в действительности, они несут в себе ещё композицию? Так как в конструкторе классов MallardDuck, RubberDuck, RedheadDuck, RecoyDuck будут инициализированы переменные поведения уток:

MallardDuck() {
flyBehavior = new FlyWithWings();
quackBehavior = new Quack();
}

код уток выкладываю:
github.com/...​ter/src/Patterns/SimUDuck

P.S. Вообще, у меня ещё один вопрос по Java. Когда проходил тестирование в SoftServe был задан вопрос, возможна ли инкапсуляция без классов. Я понимаю, что инкапсуляция без классов невозможна?

Большое спасибо за ответы! :)

👍ПодобаєтьсяСподобалось0
До обраногоВ обраному0
LinkedIn
Дозволені теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter
Дозволені теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter

Мне стало не понятным, что значить инкапсулированы? Раньше, я думал, что инкапсуляция связана тесно с реализацией класса. Т.е. моя задача максимально защитить данные, скрыть реализацию от программиста-клиента и, оставить для него только интерфейс.

Не путай инкапсуляцию с сокрытием информации. Инкапсуляция по большей части — освобождение consumer-а класса от знания деталей реализации, т.е. обёртка данных и ф-й которые работают с этими данными в объекте. В то время как сокрытие информации — сокрытие внутренних деталей реализации.

Grady Booch says (in Object Oriented Analysis and Design, page 49, second edition):

«Abstraction and encapsulation are complementary concepts: abstraction focuses on the observable behavior of an object... encapsulation focuses upon the implementation that gives rise to this behavior... encapsulation is most often achieved through information hiding, which is the process of hiding all of the secrets of object that do not contribute to its essential characteristics.»

И второй вопрос, почему у потомков отношение классов только «является» судя по этой таблице, если в действительности, они несут в себе ещё композицию? Так как в конструкторе классов MallardDuck, RubberDuck, RedheadDuck, RecoyDuck будут инициализированы переменные поведения уток:

Отношение IS-A потому что классы наследуются от общего предка и могут быть приведены к базовому типу. Композиция в данном случае определена так же в базовом классе, что и изображено на рисунке. Композиция это есть делегация поведения или инкапсуляция. Т.е. по хорошему можно использовать не конструктор для инициализации конкретного поведения а принцип инверсии зависимости (Inversion of Control/Dependency Injection), когда конкретный класс реализующий поведение определяется например в рантайме или в конфигурационном файле.

P.S. Вообще, у меня ещё один вопрос по Java. Когда проходил тестирование в SoftServe был задан вопрос, возможна ли инкапсуляция без классов. Я понимаю, что инкапсуляция без классов невозможна?

Не знаю применительно к Java, но например в Ruby имеется такое понятие как Mixins, когда функциональность (читай поведение) подмешивается в классы через модули которые сами по себе есть особый вид класса. Думаю в объектно-ориентированных языках такое невозможно, в отличии от тех же функциональных или процедурных.

Кстати рекомендую книгу Гради Бутча «Объектно ориентированный анализ и проектирование». Тебе сперва нужно разобраться в концепциях без привязки к конкретному языку, лучше по схематически по UML. Потом вникнуть в такие понятие как S.O.L.I.D en.wikipedia.org/...riented_design. и только потом переходить к паттернам.

Я могу сказать одно топикстартеру(исходя из его предыдущих тем и комментариев на ДОУ): «Не гонись понять сразу все правила игры. Начни писать, что-нибудь для себя (я начинал с создания каталогизатора для своей электронной библиотеки). Со временем ты набьешь все необходимые шишки. И уже позже, читая подобную литературу ты будешь вспоминать свой код и понимать, что ты мог бы в нем исправить».

можете кинуть в личку ссылку на сорсы своей программки?

files.mail.ru

спасибо.

Мне тяжело начать писать программу, потому что я даже не представляю с чего начать. :) Паттерны, мне интересно читать, потому что лучше начинаешь понимать связь.

Поищу и скину. Но учти, что я дотНетчик со всеми вытекающими ;) + писано под Metro Style.

Мне тяжело начать писать программу, потому что я даже не представляю с чего начать.
Из книг на русском советую попробовать К. Ларман «Применение UML и шаблонов проектирования». UML в названии пусть не пугает, он там дается попутно.
Ценность ее вижу в следующем:
Автор рассказывает о создании программ с самого начала — с сбора требований. Далее — о подходах по ведению проекта (начинающий программист может пробежать их бегло, тем более что встречал вполне аргументированную критику именно на эту часть книги)
И — показывает откуда берется вообще потребность в шаблонах проектирования, и вычленяет самые самые базовые принципы — ru.wikipedia.org/wiki/GRASP показывая, ИМХО, что эти принципы лежат в основе всех остальных, конкретных шаблонов.
Если осилите — то даю 99% что тьма вопросов которые вы задали — исчезнут. Интернет хорош тем что можно сразу спросить — но при отсутствии базовых, фундаментальных знаний вопросов придется задавать тьмы, а вот приобретаемые знания не станут цельными, и «шаг в сторону» будет опять порождать вопросы, вместо умения получать ответы самому, на основе имеющихся знаний в собственном уме.
Спасибо большое за Ваш комментарий! Вот эту книгу Вы имели ввиду?

zubrilka.com.ua/details/231

Да. Третье издание, насколько знаю самое свежее. Хотя, можно и второе, если с деньгами туго ;)

ребята, в замешательстве:
s018.radikal.ru/...5017cf389fd.png

зачем indexOf использовать

public void removeObserver(WeatherObserver observer) { int i = observers.indexOf(observer); if(i >= 0) observers.remove(i);

если должно быть достаточно:

public void removeObserver(WeatherObserver observer) { observers.remove(observer);

и второе, зачем используется явное приведение типа и не используется foreach

public void notifyObserver() { for(int i = 0; i < observers.size(); i++) { WeatherObserver observer = (WeatherObserver)observers.get(i); observer.update(temperature, humidity, pressure); } }

если должно быть достаточно:

public void notifyObserver() { for(WeatherObserver observer : observers) observer.update(temperature, humidity, pressure); }

Спасибо за помощь. :)

P.S. прошу прощения, тут проблемы с форматированием :)
оригинал закомментировал, собственно код можно найти здесь

gist.github.com/...7afeb72c1a7495a

observers.remove(observer);

Здрасьте. docs.oracle.com/.../ArrayList.html
remove(int index)
Removes the element at the specified position in this list.

Нет метода с той сигнатурой, что ты указал, т.к. коллекция — не типизированная, работает только по индексу, судя по доке. Неужели у тебя билдится? :)

for(WeatherObserver observer : observers)

Рискну предположить, что в этом случае будет InvalidCastException (если, конечно, сбилдится), т.к. итерироватьбя будет не по WeatherObserver, а по int (по коллекции индексов)

p.s. Шаблоны не нужно заучивать на память до таких тонкостей, тем более не нужно заучивать их реализацию как отче наш — достаточно абстрактного понимания для чего он нужен, и что является его членами, и как они взаимодействуют. Чтобы в дальнейшем так же не вводило в заблуждение (скажем, во flyweight), почему в java нет класса Hashtable, а есть HashMap. А вот в C# есть класс Hashtable, и это значит, flyweight реализуется только на C# ? :)

Почему не типизированный? :)
ArrayList<weatherobserver>
Получается, что в этот контейнер можно поместить потомков WeatherObserver
Кстати, сама книга была написана в английском варианте 2004 году. Может быть такой возможности не было раньше? Вот Java 6 SE documentation
s018.radikal.ru/...7db99871409.png

P.S. Я пытаюсь разобраться, потому что не знаю как правильно, поэтому спрашиваю. :)

Почему не типизированный? :)

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

в этот контейнер можно поместить потомков WeatherObserver

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

Мне вот почему-то кажется, что четкая типизация — это как палка о двух концах: с одной стороны, вы отсечете потенциальные ошибки, связанные с преобразованием типов еще на стадии компилляции (тупо — не сбилдится, если типы несовместимые), не нужно будет кастить и, соответственно, можно будет избежать InvalidCastException. Но, с другой стороны, накладные расходы среды на работу с типизированной коллекцией, ИМХО, повыше будут, нежели с нетипизированной (а с вариантом неявного преобразования 100% так и будет, правда, не знаю насколько). А так у вас — тупо экземпляры, которые неявно преобразовываются тупо в Object (а в о Object можно все тупо преобразовать) Посему, нетипизированную коллекцию могли использовать чисто ради производительности. Тем более, чтобы туда другие типы чтоб загнать, и получить исключение при преобразовании — нужно очень постараться, в рамках Обозревателя.

Интерфейсы реализовывают подобие множественного наследования. :)
WeatherObserver базовый тип для тех классов, которые его реализовывают. :)

В данном случае CurrentConditionDisplay и другие наблюдатели.

Я не понимаю, о чём Вы?

Существуют типизированные и не типизированные контейнеры. Я знаю и понимаю следующее.

Не типизированный контейнер не имеет фактического типа:

ArrayList basket= new ArrayList();

в такой контейнер можно поместить объекты разных типов, например
basket.add(new Fruit());
basket.add(new Meat());

basket.add(new Milk());

и контейнер, типизированный, у которого есть фактический тип

ArrayList<meat> basket= new ArrayList<meat>();

в такой контейнер можно поместить объекты потомков мяса, например
basket.add(new Veal());

basket.add(new Sausage())

если попытаться добавить в корзину объект new Milk() произойдёт ошибка компиляции, т.к. это не соблюдение кашрута. :))))))))))

Ну в таком случае, вы должны знать и минусы и ограничения применения обощений на Джаве :)

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

weatherobserver

", но и унаследованных потомков.

Не совсем понял.
Ковариантность, это когда переопределённый метод в подклассе, может вернуть тип, производный от типа базового класса.

Не понимаю, как такое нелёгкое определение относиться к теме?

В Эккеле ещё приводится приводится пример:

class Grain {}

class Wheat exteds Grain {}

class Mill {
Grain process() { return new Grain(); }
}
class WeatMill extends Mill {
Wheat process() { return new Wheat(); }

}

ещё говорилось, что в Java 5 SE process не позволил бы вернуть Wheat, только базовый класс.

Не понимаю, как такое нелёгкое определение относиться к теме?

Так а в чем по вашему будет выигрыш использования обобщенной коллекции в данном случае?

В данном случае рассматривается контейнер в который помещаются объекты-наблюдатели у субъекта.

В объект-контейнер помещается другой объект, другими словами, отношение has-a. Всё остальное мы придумываем сами.

Ну это понятно. Ну а зачем использовать обобщение? :)

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

член класса, контейнер уже типизирован, его фактический тип WeatherObserver.
private ArrayList<weatherobserver> observers;
т.е. вытаскивая объекты из него, это будут объекты, классы которых реализуют интерфейс WeatherObserver.

Следовательно, зачем приводить к этому типу не имею понятия. Контейнер не является не типизированным и не объявлен типа Object — хотя это одно и тоже. :)

Может это ошибка авторов?

Может это ошибка авторов?

ИМХО, об сервер появился раньше, чем дженерики в джа :)

Извините, но мы все немножко гоним:

WeatherObserver observer = (WeatherObserver)observers.get(i);

в данном случае Observers нетипизированный массив, а значит в нем может храниться всё, что угодно. А компилятору надо точно знать, что переменной observer передается ссылка на объект типа Observer. Вот поэтому и используется принудительный каст.

Почему не типизированный, объясните пожалуйста.

Это, так, лично для меня. :)

Ну далась вам эта типизация :) Если уж вам так кажется, что в книге лажа (повторюсь — тот источник это не сборник кода для копи-пэйст) — так типизировать в данном случае правильно не по конкретному классу, а по интерфейсу (конкретно — docs.oracle.com/.../Observer.html, который должен быть имплементирован в WeatherObserver. И вот это почитайте: docs.oracle.com/...Observable.html

ну я почем знаю, почему так :) видимо потому, что каждый точит как он хочет в книге рассматривается общий принцип.

100%, имхо абсолютно непонятно зачем автор топика углубляется в нюансы реализации примера: пример работает? — работает. Пример дает представление об изучаемом паттерне? — дает. Применение generic-ов или цикла for-each как то изменит суть паттерна Наблюдатель? — нет. Так pourquoi? :)

Мне стало не понятным, что значить инкапсулированы? Раньше, я думал, что инкапсуляция связана тесно с реализацией класса. Т.е. моя задача максимально защитить данные, скрыть реализацию от программиста-клиента и, оставить для него только интерфейс.

То что вы описали это лишь частный случай использования инкапсуляции. А на самом деле инкапсуляция это гораздо более общее и многогранное понятие.

В частности, паттерн «Стратегия» (речь я так понимаю идет о нем?) говорит нам об инкапсуляции поведения — мы выделям поведение которое может меняться (различные алгоритмы quack и fly) в отдельные классы, скрыв за общим интерфейсом (соответственно QuackBehavior и FlyBehavior) его реализацию от клиента, которым в данном случае выступает иерархия классов Duck.

И второй вопрос, почему у потомков отношение классов только «является» судя по этой таблице, если в действительности, они несут в себе ещё композицию? Так как в конструкторе классов MallardDuck, RubberDuck, RedheadDuck, RecoyDuck будут инициализированы переменные поведения уток:
MallardDuck() {
flyBehavior = new FlyWithWings();
quackBehavior = new Quack();
}
Потому что поля эти унаследованы ими от базового класса.

К тому же можно ( и нужно) классы потомков сделать вообще независимыми от конкретной реализации QuackBehavior и FlyBehavior, изменяя их поведение динамически. Т.е. не через инициализацию в конструкторе, а через отдельный метод вроде setFlyBehavior(FlyBehavior flyBehavior).

P.S. Вообще, у меня ещё один вопрос. Когда проходил тестирование в SoftServe был задан вопрос, возможна ли инкапсуляция без классов. Я понимаю, что инкапсуляция без классов невозможна?

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

Резюме: имхо вы абсолютно не правильно подошли к изучению паттернов. Потому что во-первых прежде необходимо до конца разобраться в базовых концепциях ООП, а во-вторых следует стараться понять в целом идею, назначение и способ реализации конкретного паттерна, а не распыляться на частности, задумываясь например о формулировках.

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

Нет, там имели виду относительно языка Java.

Потому что во-первых прежде необходимо до конца разобраться в базовых концепциях ООП

Я считал, что в базовых концепциях уже разобрался.

во-вторых следует стараться понять в целом идею, назначение и способ реализации конкретного паттерна

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

s017.radikal.ru/...b48a8f64a85.png

Проверьте, пожалуйста!
У меня хорошо усваивается материал? :)
s017.radikal.ru/...46162c72fe8.png

gist.github.com/...587b9dcb19c9cfc

p.s. порекомендуйте книгу по ООП.

вы абсолютно не правильно подошли к изучению паттернов

И как это через несколько дней вы уже к Обозревателю подошли? :) Нужно сначала изучать порождающие (потому-что их 5), потом — структурные (их 7 — уже будет легче), а после уже — поведенческие (тут уже пойдет как по маслу). Если в алфавитном порядке — то вы недавно вроде заморачивались последним порождающим — Синглтоном, а структурные как будто перепрыгнули :)

Я читаю книгу, она начинается с паттерна «Стратегия», потом идёт паттерн «Наблюдатель» :)
Сегодня, я переключился на Java, хочу добить Exceptions. :)

OMG (в хорошем смысле этого слова :)

P.S. Вообще, у меня ещё один вопрос. Когда проходил тестирование в SoftServe был задан вопрос, возможна ли инкапсуляция без классов. Я понимаю, что инкапсуляция без классов невозможна?

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

Инкапсуля́ция — свойство языка программирования, позволяющее пользователю не задумываться о сложности реализации используемого программного компонента (то, что у него внутри), а взаимодействовать с ним посредством предоставляемого интерфейса (публичных членов — методов, данных etc.), а также объединить и защитить жизненно важные для компонента данные. При этом пользователю предоставляется только спецификация (интерфейс) объекта.

ru.wikipedia.org/...ограммирование

Так же есть и такое определение: A language construct that facilitates the bundling of data with the methods (or other functions) operating on that data. en.wikipedia.org/...ed_programming

Підписатись на коментарі