Под интерфейсами имею в виду базовые классы с абстрактными виртуальными функциями, а под делегатами всякие Loki::Functor/boost::function/Winnie::Closure.
Что когда лучше использовать?
Когда виртуальные функции абсолютно не катят, и можно использовать только функторы?
Пока вижу только одно преимущество делегатов — их легко менять в runtime.
Но базовые абстрактные классы легче отписывать
(Гы, попробуйте отписать указатель на свой метод bind(&Foo::Bar, this) из std::list<boost::function>. Как это сделать?)
Есть еще "псевдо-преимущество" — можно подписать чужой класс на событие. Но это редко нужно, и легко реализуется своей виртуальной функцией — переходником.
Perfomance issues неинтересны, вроде бы они понятны.
(большие vtbl при использовании очень больших интерфейсов, динамическая аллокация памяти при использовании boost::function).
Еще в boost::function можно засунуть не только пары (объект, метод) но и любой функтор, это мне тоже не интересно, интересны именно (объект, метод).
Правильно работающая программа — просто частный случай Undefined Behavior
_W>Пока вижу только одно преимущество делегатов — их легко менять в runtime. _W>Но базовые абстрактные классы легче отписывать
Ну да? Нука привели пример
_W>(Гы, попробуйте отписать указатель на свой метод bind(&Foo::Bar, this) из std::list<boost::function>. Как это сделать?)
Вообще-то для меня делегаты — это не std::list<boost::function>, а именно boost::signals. Отписыватся в них можно элементарно.
Все плюсы и минусы функторов и интерфейсов познаёшь тогда, когда есть возможность использовать и то и другое (например, попробуй попрограммить немного на яве, чтобы прочувсвтвовать идею интерфейсов)
Здравствуйте, ArtDenis, Вы писали:
_W>>Пока вижу только одно преимущество делегатов — их легко менять в runtime. _W>>Но базовые абстрактные классы легче отписывать AD>Ну да? Нука привели пример
list.remove(this)
_W>>(Гы, попробуйте отписать указатель на свой метод bind(&Foo::Bar, this) из std::list<boost::function>. Как это сделать?) AD>Вообще-то для меня делегаты — это не std::list<boost::function>, а именно boost::signals. Отписыватся в них можно элементарно.
Ага. А для меня boost::signals это ужас на крыльях свопа.
Попробуй написать логгинг использования динамической аллокации памяти. То, что он жрет пол-килобайта памяти на каждый функтор и делает кучу динамических аллокаций даже при вызове сингнала, для меня неприемлемо.
AD>Все плюсы и минусы функторов и интерфейсов познаёшь тогда, когда есть возможность использовать и то и другое (например, попробуй попрограммить немного на яве, чтобы прочувсвтвовать идею интерфейсов)
Расскажи
Правильно работающая программа — просто частный случай Undefined Behavior
Здравствуйте, _Winnie, Вы писали:
AD>>Все плюсы и минусы функторов и интерфейсов познаёшь тогда, когда есть возможность использовать и то и другое (например, попробуй попрограммить немного на яве, чтобы прочувсвтвовать идею интерфейсов) _W>Расскажи
Программировал на Java в течении полугода в рамках полугодового курса у нас на факультете, писали простые учебные задачки, вроде "визуализация сортировки в нескольких параллельных потоках." Больше её не касался. Ну, интерфейсы. Что такого?
Правильно работающая программа — просто частный случай Undefined Behavior
AD>>Ну да? Нука привели пример _W>list.remove(this)
Блин, точно
_W>Ага. А для меня boost::signals это ужас на крыльях свопа. _W>Попробуй написать логгинг использования динамической аллокации памяти. То, что он жрет пол-килобайта памяти на каждый функтор и делает кучу динамических аллокаций даже при вызове сингнала, для меня неприемлемо.
Если честно, то меня реализация не особо заботит. Если понадобиться быстродействие — напишу свой велосипед.
AD>>Все плюсы и минусы функторов и интерфейсов познаёшь тогда, когда есть возможность использовать и то и другое (например, попробуй попрограммить немного на яве, чтобы прочувсвтвовать идею интерфейсов) _W>Расскажи
А что рассказывать.
1. Негибкая эта система. Какждый раз надо писать реализацию интерфейса в виде класса и реализовывать какой-то там метод. Нельзя повесить обработчик разных событий на один метод (бывает нужно)
2. Читабельность кода в случае с делегатами гораздо выше (на мой взгляд)
IHMO. Идея использования интерфейсов для реализации обработчика события потому и возникла, что не было другого механизма для реализации. В C++ или в C# есть более удобное средство для этого дела. Почему бы его и не использовать?
Здравствуйте, _Winnie, Вы писали:
_W>Пишу либу. Хочу дать возможность юзеру кастомизировать что-то кэллбеком. Например, куда писать лог.
_W>Как лучше сделать?
_W>
Hello, _Winnie! You wrote on Sun, 04 Dec 2005 22:09:17 GMT:
W> _W>Пишу либу. Хочу дать возможность юзеру кастомизировать что-то W> кэллбеком. Например, куда писать лог.
Такой вопрос: а что будет логироваться в этой либе? Есть подозрения, что всё это можно реализовать без помощи калбэков.
Наследование (и виртуальные функции соответственно) является одним из основных инструментов, применяемых
в ОО парадигме. Использование виртуальных функций позволяет отделить абстракцию от ее конкретизаций/реализаций.
Таким образом наследование и виртуальные финкции — это фундаментальная фича языка С++ без которой невозможно
(читай очень геморно) писать объектно-ориентированные программы.
Функторы из области обобщенного программирования: очевидные применения — аргументы алгоритмов и т.п. Использование
функторов для формирования интерфейса неправильно (по крайней мере очень не в духе С++).
Здравствуйте, _Winnie, Вы писали:
_W>Под интерфейсами имею в виду базовые классы с абстрактными виртуальными функциями, а под делегатами всякие Loki::Functor/boost::function/Winnie::Closure.
_W>Что когда лучше использовать? _W>Когда виртуальные функции абсолютно не катят, и можно использовать только функторы?
_W>Пока вижу только одно преимущество делегатов — их легко менять в runtime. _W>Но базовые абстрактные классы легче отписывать _W>(Гы, попробуйте отписать указатель на свой метод bind(&Foo::Bar, this) из std::list<boost::function>. Как это сделать?) _W>Есть еще "псевдо-преимущество" — можно подписать чужой класс на событие. Но это редко нужно, и легко реализуется своей виртуальной функцией — переходником.
_W>Perfomance issues неинтересны, вроде бы они понятны. _W>(большие vtbl при использовании очень больших интерфейсов, динамическая аллокация памяти при использовании boost::function).
_W>Еще в boost::function можно засунуть не только пары (объект, метод) но и любой функтор, это мне тоже не интересно, интересны именно (объект, метод).
Здравствуйте, bada, Вы писали:
B>imho, на лицо некоторое непонимание основ.
Просто человек хочет сказать, что калбэки можно реализовать при помощи функторов, а можно при помощи интерфейсов. При помощи интерфейсов это выглядит так:
При помощи интерфейсов:
class CallbackInterface
{
public:
virtual void do_call() = 0;
};
class ComeClass // Класс, который инициирует событие
{
CallbackInterface * cbi;
void do_something()
{
//if (cbi) cbi->do_call(); // инициируем событие
//
}
public:
void set_call_back_obj(CallbackInterface * cbi)
{
this->cbi = cbi;
}
};
class Eventhandler : public/* или protected (чтобы не нарушать принцип Лисков)*/ CallbackInterface
{
void init_events()
{
// Указываем, что обработчиком события явсляется наш объект
some_class.set_call_back_obj(this);
}
private:
// Обработчик событияvirtual void do_call()
{
}
};
При помощи функторов:
class ComeClass
{
void do_something()
{
//
event(); // инициируем событие
//
}
public:
boost::signal<void()> event;
};
class Eventhandler
{
boost::signals::scoped_connection scp_conn;
void init_events()
{
// Указываем, что обработчиком события является this->do_event
scp_conn = some_class.event.connect( boost::bind(&Eventhandler::do_event, this) );
}
private:
// Обработчик событияvoid do_event()
{
}
};
Причём в последнем случае при помощи boost::signals::scoped_connection, объект отпишется от события после удаления автоматически. IMHO даже по объёму кдоа и читабельности делегаты (функторы) лучше
On Mon, 05 Dec 2005 05:21:41 -0000, bada <48802@users.rsdn.ru> wrote:
> imho, на лицо некоторое непонимание основ. > > Наследование (и виртуальные функции соответственно) является одним из основных инструментов, применяемых > в ОО парадигме. Использование виртуальных функций позволяет отделить абстракцию от ее конкретизаций/реализаций.
OO парадигма — это не серебряная пуля. Полезность сильно преувеличена.
> Таким образом наследование и виртуальные финкции — это фундаментальная фича языка С++ без которой невозможно > (читай очень геморно) писать объектно-ориентированные программы.
Это ерунда. Ознакомься с реализацией VFS в Lunux'e: OO на чистом C.
Здравствуйте, Smart_Ass, Вы писали:
S_A>Что такое функтор ? По названию похоже что это объект-функция.
Функтор это объект к которому можно применить операцию скобки.
В С++ это может быть:
1. функция.
2. указатель на функцию.
3. объект класса с перегруженной операцией скобки.
Здравствуйте, MaximE, Вы писали:
>> imho, на лицо некоторое непонимание основ. >> >> Наследование (и виртуальные функции соответственно) является одним из основных инструментов, применяемых >> в ОО парадигме. Использование виртуальных функций позволяет отделить абстракцию от ее конкретизаций/реализаций.
ME>OO парадигма — это не серебряная пуля. Полезность сильно преувеличена.
>> Таким образом наследование и виртуальные финкции — это фундаментальная фича языка С++ без которой невозможно >> (читай очень геморно) писать объектно-ориентированные программы.
ME>Это ерунда. Ознакомься с реализацией VFS в Lunux'e: OO на чистом C.
Отвечу немного в сторону. По поводу колбэков.
Мы устанавливаем связь между двумя объектами — клиентом и исполнителем. Здесь есть 3 подзадачи
— установление связи: последнее слово здесь за исполнителем (а иногда и первое)
— обратный вызов
— разрыв связи
Если время жизни связи ограничено (до выхода из функции, куда колбек был передан как параметр) или управляется только исполнителем (подписка/отписка), то всё, что нужно клиенту — это функциональность обратного вызова. Можно через биндинг, можно через интерфейс.
А если разрыв связи может быть инициирован клиентом — то возникают вопросы
— извещения исполнителя
— управления временем жизни исполнителя
Можно, конечно, решить их с помощью ещё одного-двух колбеков... Однако, поскольку эти функции относятся к одному объекту — есть смысл их сгруппировать в единый интерфейс.
Причём необязательно, чтобы этот интерфейс был реализован напрямую исполнителем. Между ним и клиентом может располагаться объект-прокси/адаптер.
Здравствуйте, bada, Вы писали:
B>imho, на лицо некоторое непонимание основ.
B>Наследование (и виртуальные функции соответственно) является одним из основных инструментов, применяемых B>в ОО парадигме. Использование виртуальных функций позволяет отделить абстракцию от ее конкретизаций/реализаций. B>Таким образом наследование и виртуальные финкции — это фундаментальная фича языка С++ без которой невозможно B>(читай очень геморно) писать объектно-ориентированные программы.
Если уж лезть в основы, то наследование не является частью ООП-парадигмы.
Как наследование от абстрактной базы, так и использование делегатов позволяют определить протокол для взаимодействия с объектом (то есть набор сообщений, которые в данный момент имеют для объекта смысл).
Для первого подхода имеет место статическое определение объектом поддерживаемого протокола (и как следствие возможность проверки типов на этапе компиляции), для второго — возможность динамически изменять протокол объектом (тем не менее, сами сообщения должны быть известны в compile-time).
-- Андрей
Re: Интерфейсы vs Делегаты.
От:
Аноним
Дата:
19.12.05 15:26
Оценка:
Извиняюсь
Кто-то тут наежал на Жабу, но вы не совсем правы, она позволяет создавать анонимные внутрение классы, а для больших листенеров (интерфейсов, предназначенных для подключения к событиям) разработчики создают адаптеры (пустую реализацию)
Все это очень упрощает использование интерфейсов вместо функторов/делегатов Сори если кого-то обидел