RMihael
Гость
|
|
« : 13-09-2003 08:53 » |
|
Как-то раз у меня возникла проблема -- как хранить в одном контейнере разнотипные элементы с частично несовместимыми интерфесами (в основном отличались только типом возвращаемого значения -- например pair<int,int> get_lims() и int get_lims() ) и единообразно их обрабатывать. Типы не имели общего родительского и ввести его естественным образом было нельзя. Тогда эту проблему я решил использованием Discriminated Union Александреску, но осадок на душе остался -- может можно было как-то попроще? Может кто-то сталкивался с такой проблемой раньше и успешно её решал? Напишите, плз.
|
|
|
Записан
|
|
|
|
NetRaider
Гость
|
|
« Ответ #1 : 15-09-2003 04:59 » |
|
Если интерфейсы можно модифицировать, тогда смотри в сторону Double Dispatch или паттерна Visitor.
|
|
|
Записан
|
|
|
|
Serega
Гость
|
|
« Ответ #2 : 19-09-2003 19:48 » |
|
Можно адаптер написать и через него работать
|
|
|
Записан
|
|
|
|
RMihael
Гость
|
|
« Ответ #3 : 20-09-2003 07:48 » |
|
Извините, что не отвечал, был в коммандировке. Двойная диспетчеризация и Visitor не подходили, поскольку объекты надо было хранить в контейнере. Адаптор не подходил, поскольку интерфейсы отличались возвращаемыми типами -- или есть какой-то неочевидный финт?
|
|
|
Записан
|
|
|
|
Serega
Гость
|
|
« Ответ #4 : 20-09-2003 08:00 » |
|
Для того адаптер и нужен чтобы адаптировать интерфейс к единому
|
|
|
Записан
|
|
|
|
RMihael
Гость
|
|
« Ответ #5 : 20-09-2003 12:33 » |
|
Для того адаптер и нужен чтобы адаптировать интерфейс к единому Давай с кодом, для избежания недоразумений. У меня было что-то на манер этого: class StrictParameter { public: const string& get_val(void) const; const list<string>& get_lims(void) const; } class RangeParameter { public: double get_val(void) const; const pair<double, double>& get_lims(void) const } Какой адаптер ты предлагаешь? PS: Насколько я помню этот шаблон там предполагается создание одного адаптера для одного класса. Как их тогда хранить в контейнере? Да и усложняет это здорово -- дополнительных классов много.
|
|
|
Записан
|
|
|
|
Serega
Гость
|
|
« Ответ #6 : 20-09-2003 19:20 » |
|
Классов не будет много, а сложностей будет только меньше, если это действительно нужно Этим кодом ты мне ничего не сказал Как это используется в программе ? надо исходить из этого, а пока не понятно вообще для чего эти классы обьединять
|
|
|
Записан
|
|
|
|
RMihael
Гость
|
|
« Ответ #7 : 21-09-2003 10:51 » |
|
Корень зла в том, что эти разнородные объекты надо было хранить в одном контейнере. При создании адаптера для каждого из классов хранить их так не получится. Если приводить эти адаптеры к одному, например использовать fake vtable или переключение типов, то это достаточно сложно и тяжело расширяемо. Если есть какой-то простой способ сделать из нескольких адаптеров один то скажи, я такого придумать не могу.
|
|
|
Записан
|
|
|
|
Serega
Гость
|
|
« Ответ #8 : 21-09-2003 11:12 » |
|
Из того что-ты мне написал я вижу что типы никак не связаны Возможно проблема как-раз в том, что ты собрался хранить обьекты этих типов вместе Обьясни более подробно что ты хочешь реализовать, тогда и ответ будет более развернутым
|
|
|
Записан
|
|
|
|
RMihael
Гость
|
|
« Ответ #9 : 21-09-2003 13:16 » |
|
Из того что-ты мне написал я вижу что типы никак не связаны Возможно проблема как-раз в том, что ты собрался хранить обьекты этих типов вместе Обьясни более подробно что ты хочешь реализовать, тогда и ответ будет более развернутым Типы были связаны, но на концептуальном уровне, а не средствами языка. Каждый класс представлял собой некоторый диапазон(численный) или возможное качественное значение определённого параметра. Соответственно в классе параметра был контейнер с его диапазонами возможных значений. Частично интерфейсы совпадали, и это совпадение хотелось использовать прозрачно, но не обрезая остальной части интерфейса. Ситуация усугублялась тем, что на момент проектирования была большая вероятность того, что появится новый тип возможных значений (как позже и случилось).
|
|
|
Записан
|
|
|
|
Serega
Гость
|
|
« Ответ #10 : 21-09-2003 15:08 » |
|
Пример. У нас есть класс A, мы с ним работаем и храним обьекты этого класса в контейнере Потом у нас появляется класс B, с которым нам приходится работать и хранить обьекты вместе с обьектами класса А Насколько я понимаю это означает что классы A и B для клиента неразличимы, т.к. работать он с ними будет одинаково (об обратном не сказано ни слова) Первое что приходит на ум в данной ситуации это адаптер, что-то типа class AdaptBtoA { public A | B* inner; public{ AdaptBtoA)B* toAdapt: { inner)toAdapt: |" ... // Переадресация вызовов inner "
Если же при добавлении B придется править клиента, тогда лучше использовать визитор ИМХО
|
|
|
Записан
|
|
|
|
NetRaider
Гость
|
|
« Ответ #11 : 22-09-2003 00:35 » |
|
Типы были связаны, но на концептуальном уровне, а не средствами языка. Каждый класс представлял собой некоторый диапазон(численный) или возможное качественное значение определённого параметра. Соответственно в классе параметра был контейнер с его диапазонами возможных значений. Частично интерфейсы совпадали, и это совпадение хотелось использовать прозрачно, но не обрезая остальной части интерфейса. Ситуация усугублялась тем, что на момент проектирования была большая вероятность того, что появится новый тип возможных значений (как позже и случилось).
Если типы были связаны на концептуальном уровне, то почему эта связь не отражена в коде ? Базовые классы надо вводить - и при добавлении новых типов заморочек не будет. Можно, конечно изобрести свой метод хранения разных типов в контейнере, но пострадает быстродействие, читабельность, и типобезопасноть. Если действительно надо то как вариант boost::any + свой аналог RTTI. Хотя необходимость определения типа в runtime, IMHO design fault.
|
|
|
Записан
|
|
|
|
RMihael
Гость
|
|
« Ответ #12 : 25-09-2003 18:21 » |
|
Если типы были связаны на концептуальном уровне, то почему эта связь не отражена в коде ? Базовые классы надо вводить - и при добавлении новых типов заморочек не будет. Связи на уровне языка не было из-за того самого design fault. Когда выяснилось, что их надо смешивать то было уже два отдельных класса и много кода. Базовый класс вводить я не стал, потому что проблема меня захватила и решение я нашёл -- оказалось достаточно удобное, хотя и не без недостатков. Просто сейчас стало интерестно, кто как решал такие проблемы. Если действительно надо то как вариант boost::any + свой аналог RTTI. Я использовал Discriminated Union -- типобезопастный и более мощный вариант boost::any. Хотя необходимость определения типа в runtime, IMHO design fault. Ну если тщательно заинкапсулировать, то можно. Дельфисты, Явщики используют и не ругаются. Хотя некрасиво конечно.
|
|
|
Записан
|
|
|
|
RMihael
Гость
|
|
« Ответ #13 : 25-09-2003 19:15 » |
|
...Первое что приходит на ум в данной ситуации это адаптер, что-то типа... Скорее всего как-то так я бы и поступил, если бы у меня сначала был один класс, а потом добавился второй. С самого начала было два класса, а необходимость связать их появилась гораздо позже, так что в первоначальном проекте их связь не была показана (за что и пострадал в конце концов). А метод хорош, только я бы сделал некий абстрактный интерфейс, и адаптировал бы к нему. Различия интерфейса спрятал бы в Visitor. Вот разве что дополнительные классы мне не очень нравятся, но за всё приходится платить. Спасибо за помощь, возможно совет пригодится в будущем
|
|
|
Записан
|
|
|
|
|