Может быть кто-то уже сталкивался с такой проблемой, подскажите красивое решение.
Есть класс А, у которого есть вложенный класс, у которого есть еще вложенный класс и т.д. до класса В. Наследование не используется, только аггрегация.
И вот выясняется, что "потомку" n-го колена В понадобился один из параметров прародителя А. (Этот параметр неизменный в течение всей жизни всех объектов.)
Как ему его получить?
1) Передавать вниз по дереву в процессе создания вложенных объектов
2) То же что 1, только в обратную сторону — запрашивать параметр у родителя по цепочке вверх
3) Объявить параметр как глобальные данные
В случаях 1) и 2) нарушается инкапсуляция.
В 1) все классы выше В по иерархии должны знать, что существует какой-то там дальний потомок В и ему понадобится этот параметр и будут передавать его вниз, никак по-другому не используя. Кроме того, завтра я могу сделать другой класс В, которому параметр уже не будет нужен (или не дай бог нужен еще один параметр ), что в таком случае — всю иерархию переделывать?
В 2) наоборот, класс В должен знать что у его непосредственного предка есть еще предок, у которого тоже есть предок и т.д. до А, у которого есть необходимый параметр. В принципе, потомки должны знать своих предков , но если в цепочку предков добавится промежуточное звено, то всех потомков вроде В нужно будет об этом уведомить (переделать). Кроме того, не очень хочется открывать ВЕСЬ интерфейс предка какому-то там потомку только из-за одного параметра.
Вариант 3) не подходит, так как экземпляров А может быть несколько. Даже если бы он был один, все равно не особо красивое решение.
Вот сижу и думаю, мож какой-то паттерн на этот случай есть?
Здравствуйте, Ignoramus, Вы писали:
I>Есть класс А, у которого есть вложенный класс, у которого есть еще вложенный класс и т.д. до класса В. Наследование не используется, только аггрегация. I>И вот выясняется, что "потомку" n-го колена В понадобился один из параметров прародителя А. (Этот параметр неизменный в течение всей жизни всех объектов.) I>Как ему его получить?
Сдаётся мне, что ошибка в иерархии. Если B имеет какие-то отношения с A, то видимо они расположены неверно в вашей агрегатной цепочке. Так что, я бы стал думать не над способами передачи, а над способами реорганизации структуры.
Re[2]: Передача параметра вниз по иерархии классов
VS>Сдаётся мне, что ошибка в иерархии. Если B имеет какие-то отношения с A, то видимо они расположены неверно в вашей агрегатной цепочке. Так что, я бы стал думать не над способами передачи, а над способами реорганизации структуры.
Пожалуй, соглашусь: что-то тут не так. А слобо ли автору привести описание задачки? Поди, на более конкретном примере было бы яснее.
Да хранит вас господь в сухом прохладном месте...
Re[2]: Передача параметра вниз по иерархии классов
Здравствуйте, Vadim S., Вы писали:
VS>Здравствуйте, Ignoramus, Вы писали:
I>>Есть класс А, у которого есть вложенный класс, у которого есть еще вложенный класс и т.д. до класса В. Наследование не используется, только аггрегация. I>>И вот выясняется, что "потомку" n-го колена В понадобился один из параметров прародителя А. (Этот параметр неизменный в течение всей жизни всех объектов.) I>>Как ему его получить?
VS>Сдаётся мне, что ошибка в иерархии. Если B имеет какие-то отношения с A, то видимо они расположены неверно в вашей агрегатной цепочке. Так что, я бы стал думать не над способами передачи, а над способами реорганизации структуры.
Конкретизирую задачу.
Класс А это контрол. Он состоит из нескольких элементов, каждый из которых реализован в виде класса. Каждый из элементов составной, т.е. состоит из подэлементов. Все элементы и подэлементы могут заменяться другими (смена скина, паттерн бридж). Один из подэлементов — текстовое поле, которому для измерения необходимой высоты области по событию изменение ширины требуется hwnd контрола, в виду особенности измеряющей WINAPI ф-ции.
Имхо очень удачная структура не вижу как можно еще ее реорганизовать... Есть соображения?
Re[3]: Передача параметра вниз по иерархии классов
I>Класс А это контрол. Он состоит из нескольких элементов, каждый из которых реализован в виде класса. Каждый из элементов составной, т.е. состоит из подэлементов. Все элементы и подэлементы могут заменяться другими (смена скина, паттерн бридж). Один из подэлементов — текстовое поле, которому для измерения необходимой высоты области по событию изменение ширины требуется hwnd контрола, в виду особенности измеряющей WINAPI ф-ции.
То есть контрол владеет другими контролами, среди которых в этой цепочке владения текстовое поле? Или кто такие эти подэлементы?
Да хранит вас господь в сухом прохладном месте...
Re[4]: Передача параметра вниз по иерархии классов
Здравствуйте, Козьма Прутков, Вы писали:
I>>Класс А это контрол. Он состоит из нескольких элементов, каждый из которых реализован в виде класса. Каждый из элементов составной, т.е. состоит из подэлементов. Все элементы и подэлементы могут заменяться другими (смена скина, паттерн бридж). Один из подэлементов — текстовое поле, которому для измерения необходимой высоты области по событию изменение ширины требуется hwnd контрола, в виду особенности измеряющей WINAPI ф-ции.
КП>То есть контрол владеет другими контролами, среди которых в этой цепочке владения текстовое поле? Или кто такие эти подэлементы?
Да, примерно так. Контрол — владеет другими "контролами", например в окне есть несколько айтемов, каждый из которых состоит из иконки и текстового поля.
Однако HWND на них всех только один — контейнерский. Все остальные — просто объекты (поэтому я назвал суб-"контролы" в кавычках)
Re[5]: Передача параметра вниз по иерархии классов
I>Да, примерно так. Контрол — владеет другими "контролами", например в окне есть несколько айтемов, каждый из которых состоит из иконки и текстового поля. I>Однако HWND на них всех только один — контейнерский. Все остальные — просто объекты (поэтому я назвал суб-"контролы" в кавычках)
Посмотри на модели элементов управления во всяких библиотеках, например, в WinForms. Как там сделано? Контрол имеет родителя, те, кто не имеет, на форме не рисуются (о них никто не знает). Вот и все.
Конрол имеет родителя, хочешь ты того или нет: он контрол и принадлежит либо другому контролу либо форме.
Кстати, кто у тебя создает подчиненные контролы? Их непосредственные контейнеры? Нет возможности высунуть свойство, типа, Parent (или Container) и туда совать правильный объект-контейнер?
Да хранит вас господь в сухом прохладном месте...
Re[6]: Передача параметра вниз по иерархии классов
Здравствуйте, Козьма Прутков, Вы писали:
I>>Да, примерно так. Контрол — владеет другими "контролами", например в окне есть несколько айтемов, каждый из которых состоит из иконки и текстового поля. I>>Однако HWND на них всех только один — контейнерский. Все остальные — просто объекты (поэтому я назвал суб-"контролы" в кавычках) КП>Посмотри на модели элементов управления во всяких библиотеках, например, в WinForms. Как там сделано? Контрол имеет родителя, те, кто не имеет, на форме не рисуются (о них никто не знает). Вот и все.
КП>Конрол имеет родителя, хочешь ты того или нет: он контрол и принадлежит либо другому контролу либо форме.
Контрол-то имеет родителя, но сам ребенок об этом не знает. Знает только родитель, т.е. знание только в одну сторону, и не хотелось бы делать знание двусторонним без необходимости.
КП>Кстати, кто у тебя создает подчиненные контролы? Их непосредственные контейнеры? Нет возможности высунуть свойство, типа, Parent (или Container) и туда совать правильный объект-контейнер?
Подчиненные контролы создают их непосредственные контейнеры и сохраняют их внутри себя.
Мне тоже пришла эта идея — см. мой первый пост, №2, там же и ее недостатки.
А другого ничего нельзя придумать?
Re[5]: Передача параметра вниз по иерархии классов
Здравствуйте, Ignoramus, Вы писали:
I>Однако HWND на них всех только один — контейнерский. Все остальные — просто объекты (поэтому я назвал суб-"контролы" в кавычках)
Если контрол содержит другие контролы, то он уже тоже контейнер.
Если у вас есть контейнеры-псевдоконтролы, то значит они должны возвращать hwnd своего парента.
В таком случае каждый контрол сможет получить hwnd контейнера, который и есть реальный контрол.
А вообще, если уж вы пользуете bridge и хотите меньшую зависимость от реализации оконной системы, то я бы посоветовал не использовать hwnd в интерейсах. Может тогда уж лучше что-то вроде GetRect() или т.п. Потому что, может случиться так, что вы захотите, чтобы какой-нибудь псевдо-контейнер выдавал свои собственные размеры по какому-то новому алгоритму, не зависимо от hwnd его родителя.
В случае с hwnd вы неявно нарушаете инкапсуляцию, получая размеры в обход класса контейнер через внешнее API.
Re[6]: Передача параметра вниз по иерархии классов
Здравствуйте, Vadim S., Вы писали:
VS>Здравствуйте, Ignoramus, Вы писали:
I>>Однако HWND на них всех только один — контейнерский. Все остальные — просто объекты (поэтому я назвал суб-"контролы" в кавычках)
VS>Если контрол содержит другие контролы, то он уже тоже контейнер. VS>Если у вас есть контейнеры-псевдоконтролы, то значит они должны возвращать hwnd своего парента. VS>В таком случае каждый контрол сможет получить hwnd контейнера, который и есть реальный контрол.
Контейнеры-псевдоконтролы пока что не знают ничего о своем родителе, включая его hwnd, и я хотел бы избежать этого знания.
VS>А вообще, если уж вы пользуете bridge и хотите меньшую зависимость от реализации оконной системы, то я бы посоветовал не использовать hwnd в интерейсах.
Так я и не хочу! Но не получается — для того чтобы вызвать некоторые API функции (DrawText, OpenThemeData) необходимо иметь hwnd
VS>Может тогда уж лучше что-то вроде GetRect() или т.п. Потому что, может случиться так, что вы захотите, чтобы какой-нибудь псевдо-контейнер выдавал свои собственные размеры по какому-то новому алгоритму, не зависимо от hwnd его родителя.
Я хочу померять размер текста с помощью DrawText. Это намного сложнее сделать руками чем GetRect.
VS>В случае с hwnd вы неявно нарушаете инкапсуляцию, получая размеры в обход класса контейнер через внешнее API.
Согласен. Но не знаю как обойти. Как ни крути, не получается полностью избавиться от контекста использования объекта. В данном случае это hwnd->hdc.
Re[7]: Передача параметра вниз по иерархии классов
Здравствуйте, Ignoramus, Вы писали:
I>Контейнеры-псевдоконтролы пока что не знают ничего о своем родителе, включая его hwnd, и я хотел бы избежать этого знания.
I>Согласен. Но не знаю как обойти. Как ни крути, не получается полностью избавиться от контекста использования объекта. В данном случае это hwnd->hdc.
Как вариант, завести отдельный объект сцена, которому неизвестна ваша иерархия, контролы и пр.
Этот объект будет давать интерфейс взаимодействия с конкретной реализацией отображения. В вашем случае он должен уметь подсчитывать, какое пространство нужно для написания в нем текста, прорисовка простых графических примитивов (прямоугольники, линии, текст, пр.), что-либо еще (по мере необходимости).
В Windows ему нужен будет hwnd или hdc.
При прорисовке главному объекту передается эта сцена (либо же он сам ее содержит), а уже он как контейнер передает ее всем своим потомкам, чтобы они могли спокойно себя в ней нарисовать. И далее по тексту (реальные контролы-контейнеры могут содержать подсцены, например, или еще как-то).
И все.
Таким образом, у вас будет разделена ф-циональность рисования (что позволит вам рисовать скины, и т.д.) от ф-циональности отображения.
Надеюсь, я вас правильно понял.
Здравствуйте, Ignoramus, Вы писали:
I>Контрол-то имеет родителя, но сам ребенок об этом не знает. Знает только родитель, т.е. знание только в одну сторону, и не хотелось бы делать знание двусторонним без необходимости.
А как, простите, ребенок собрался жить без этого знания? Ему для нормального функционирования нужна информация об окружающей среде. В частности, hwnd того окна, на котором он собрался отображаться. Это принципиальная часть контракта этого класса.
И ее обязаны обеспечить те, кто собрался его хостить.
... << RSDN@Home 1.1.4 stable rev. 510>>
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[8]: Передача параметра вниз по иерархии классов
Здравствуйте, Sinclair, Вы писали:
S>Здравствуйте, Ignoramus, Вы писали:
I>>Контрол-то имеет родителя, но сам ребенок об этом не знает. Знает только родитель, т.е. знание только в одну сторону, и не хотелось бы делать знание двусторонним без необходимости. S>А как, простите, ребенок собрался жить без этого знания? Ему для нормального функционирования нужна информация об окружающей среде. В частности, hwnd того окна, на котором он собрался отображаться. Это принципиальная часть контракта этого класса. S>И ее обязаны обеспечить те, кто собрался его хостить.
Идея такая, чтобы изолировать дочерний объект от знания контекста, в котором он хостится. Все данные о контексте передаются ему в соответствующей функции (например, hdc в Paint()).
(Даже более того, тут звучало мнение, и я с ним скорее согласен, что доступ к хендлам родительского объекта — нарушение инкапсуляции, т.к. позволяет делать API-вызовы в обход моей иерархии. Но это скорее неизбежное зло от использования callback механизма WinAPI, и на его преодоление уходит слишком много усилий — построение своего фреймворка.)
Однако, возникла такая ситуация, когда нужно получить данные о контексте за пределами соотв. функции. Например, чтобы измерить ширину/высоту текста ф-цией DrawText(hdc, ...DT_CALCRECT...) на событие WM_SIZE, необходимо знать hdc, которого нет. Поэтому и возник сабж.
Я склоняюсь к варианту, когда каждый родитель будет передавать каждому потомку при его создании некую сущность "менеджер настроек" который будет "отвечать на все вопросы" об окружающей среде. Хоть мне это и не очень нравится с точки зрения эстетики, но такое решение вроде бы неплохое в плане сопровождения (по крайней мере пока не обнаружил проблем), например, без проблем можно добавить новый параметр окружения.
Re[8]: Передача параметра вниз по иерархии классов
Здравствуйте, Vadim S., Вы писали:
VS>Здравствуйте, Ignoramus, Вы писали:
I>>Контейнеры-псевдоконтролы пока что не знают ничего о своем родителе, включая его hwnd, и я хотел бы избежать этого знания.
I>>Согласен. Но не знаю как обойти. Как ни крути, не получается полностью избавиться от контекста использования объекта. В данном случае это hwnd->hdc.
VS>Как вариант, завести отдельный объект сцена, которому неизвестна ваша иерархия, контролы и пр. VS>Этот объект будет давать интерфейс взаимодействия с конкретной реализацией отображения. В вашем случае он должен уметь подсчитывать, какое пространство нужно для написания в нем текста, прорисовка простых графических примитивов (прямоугольники, линии, текст, пр.), что-либо еще (по мере необходимости). VS>В Windows ему нужен будет hwnd или hdc. VS>При прорисовке главному объекту передается эта сцена (либо же он сам ее содержит), а уже он как контейнер передает ее всем своим потомкам, чтобы они могли спокойно себя в ней нарисовать. И далее по тексту (реальные контролы-контейнеры могут содержать подсцены, например, или еще как-то). VS>И все. VS>Таким образом, у вас будет разделена ф-циональность рисования (что позволит вам рисовать скины, и т.д.) от ф-циональности отображения. VS>Надеюсь, я вас правильно понял.
Думаю, я понял Вашу мысль, но скорее буду вынужден прибегнуть к промежуточному компромиссному варианту. Создам объект "сцена", который предоставляет доступ к hwnd, hdc и т.п. и буду полагаться что никто из иерархии не злоупотребит этим нарушением инкапсуляции .
В противном случае необходимо каждую API ф-цию которую будет вызывать хотя бы один класс иерархии, реализовывать в виде метода сцены, скрывая таким образом hwnd hdc и т.п. В конечном итоге это приведет к подобию собственного "фреймворка" — велосипедной надстройки над WinAPI.
Объект "сцена" будет передаваться как параметр в конструктор каждому классу иерархии, как объект "знающий ответы на все вопросы".