analizer
05.10.2011 20:15 mcabber
пстач, вот тоже интересная задачка: есть два ортогональных интерфейса-стратегии. хочется создать объект, который оба их будет реализовывать, но, при этом, реализацию для каждого из них, будет получать в момент конструирования. вопрос, как реализации задавать и как этот объект конструировать? использовать PImplинг не интересно. Шаблон и наследоваться от параметров — тоже, ибо на две стратегии четыре ветки кода, а на три — уже девять.
Recommended by:
@magog
Забабахать паттерн Builder, чтобы потом сделать нечто вроде:
Builder b;
b.addIface1Impl1().addIface2Impl2();
либо
b.addIface1(new Impl1()).addIface2(new Impl2());
Скажем, если сделать просто b.addIface2Impl2();, то первый будет по-умолчанию. Методы add_ из Builder каждый раз возвращают ссылку на него (для связывания вызовов в цепочку).
Для непосредственного создания вызывать b.create(), который вернет уже готовый продукт.
ну, то есть, по сути — глупость изначального вопроса в том, что объект будет реализовывать две стратегии, при этом получая конкретную из них (до конца жизни) в момент конструирования. тогда нафига ему вторая стратегия? при builder таки получит одну-единственную.
p.s.: это я так, убедиться, что всё правильно понял. или не?
он получает две ортогональные стратегии. сделано для того, чтобы иметь единую точку входа для нескольких стратегий переданных в функцию, а не тащить стопицот разных указателей
несколько не то, но идея хорошая. можно генерить типы на лету используя подобные цепочки вызовов
Две стратегии ему, потому что они ортогональны, как я понел. Цепочка вызовов builder будет добавлять их обе, не понял фразу:
>при builder таки получит одну-единственную.
подожди. что значит ортогональные? интерфейс взаимодействия с ними, что ли, разный будет? или какая вообще разница?
Да, именно в этом соль.
две стратегии реализуют два интерфейса
при этом, доступ к методам осуществляется через один указатель
а нужная потом выбирается в рантайме, что ли? что ж это за стратегии тогда такие? (или это не те стратегии, которые паттерн?)
У тебя П(аттерн)ГМ штоле ? :)
Iface1 {
f();
}
Iface2 {
g();
}
Impl {
Iface1 * subImpl1;
Iface2 * subImpl2;
f() {
subImpl1→f();
}
g() {
subImpl2→g();
}
}
-- я так понял мысль афтара (на псевдокоде).
да не выбирается ничего. просто в функции может быть нужно std::vector<char> IDataStorage::GetData(const char* key); и void ILogger::Log(const char* str); а также over 9000 других интерфейсов. и мне хочется все эти интерфейсы передать как указатель на IWorkingEnvironment, чтобы через него всё делать
и потом делать work_env→logger→log() и work_env→data_storage→get_data() и т.п.? (уж прости, мне реально интересно)
work_env→log() & work_env→get_data(). пример я выбрал неудачный, но на практике две вызываемые функции вполне уместны в рамках одного интерфейса
блеать. короче:
- "два ортогональных интерфейса-стратегии" означает, что они никак друг с другом не связаны, так? (логгер и получатель данных, как в примере выше)
- выбор каждого из них (привязка) происходит при конструировании
в этом задача? :-)
задача в том, чтобы под одним указателем объединить два интерфейса, с прямым доступом к их методам
при этом, просто унаследоваться от обоих и сделать реализацию нельзя — ибо поинтер на реализацию придёт снаружи
Можно сделать здесь Impl наследником от обоих интерфейсов, тогда он вполне сможет везде за них сойти. Декоратор это называется, насколько я помню.
надо подумать
аа, понял. вопрос, как реализовать этот work_env. так? а чем не подойдёт /12? или описывать каждый метод руками в Impl не кошерно?
не кошерно
Зделой оператор приведения типа к интерфейсу, это же плюсы!
operator Iface1 () {
return subImpl1;
}
ладно. как по мне — кошерно, потому что читать код понятнее (пошел в Impl, увидел функции, увидел куда ведут :-) типа как app→log() делает this→logger→log() — очень понятно всё.
Мля, местами переставил в сигнатуре сущности.
очень уж меня будет напрягать дублирование имён типов в:
class IWorkingEnvironment: public IDataStorage, public ILogger {
std::unique_ptr<IDataStorage> DataStorage;
std::unique_ptr<ILogger> Logger;
public:
...
std::vector<char> GetData(const char* key) {
Data→GetData(key);
}
void Log(const char* str) {
Logger→Log(str);
}
};
а взлетит?
ща попробую
Не взлетит, если наследоваться от этих же интерфейсов.
Ну и нельзя будет вызвать как:
Impl x;
x→f();
Зато можно будет передать туда, где ждут iface1, ну и зделоть
Iface xif = x;
xif→f();
В общем, я бы не ебал моцк и делал:
x→getIface1()→f();
Так хоть семантически понятно, что ты и от кого хочешь.
Возвращать, конечно же, shared_ptr через getIface1() ;)
совсем не взлетит. ибо класс должен наследоваться от IWorkingEnvironment, в котором чисто виртуальные методы, которые таки придётся переопределить
Архитектуропроблемы, да.
Выбирай, в общем.