Функционально идентичные используют обратный вызов для настройки поведения класса, а также используют виртуальную функцию и наследование. Но я считаю, что использование функциональных объектов немного более гибко в моей собственной работе.
class Animal
{
virtual void onTouch() = 0 ;
} ;
class Dog : public Animal
{
void onTouch()
{
// all dogs behave this way.
// to change I'd need to subclass, or add parameters.
puts( "Woof" ) ;
}
} ;
struct Animal
{
function<void ()> onTouch ;
} ;Animal dog1,dog2 ;
dog1.onTouch = dog2.onTouch = [](){
puts( "Woof" ) ;
} ;
Animal dog3 ;
dog3.onTouch = [](){
// slightly modified behavior from dog1 and dog2, without
// having to subclass, add members, or pass extra parameters
puts( "Arr.. Woof" ) ;
} ;
У кого-нибудь есть имя для того, что я здесь делаю или по какой-либо причине? не использовать это
Сказать, что это «немного более гибко», чем виртуальные функции, все равно, что сказать goto
немного более гибок, чем если бы / иначе. С вашей альтернативой вы можете:
Однако функциональные объекты могут не получить доступ к защищенным членам «своего» объекта, что может потребовать, чтобы вы нарушили инкапсуляцию в классе хоста, чтобы объекты функции могли делать то, что им нужно.
В целом, это кажется хорошим способом связать объект с внешнее поведение это должно быть инициировано этим объектом (например, обработчиком щелчка кнопки), но использование его для расширения присущего классу поведения кажется создающим больше проблем, которые он решает.
Наиболее распространенный пример хорошего использования этой парадигмы — для функций обратного вызова или обработчиков событий, потому что класс связан с механикой рисования кнопки и обработкой событий мыши (для класса кнопки), или с управлением сетевым протоколом (для класс сетевого сокета). В этих случаях делать обработчик событий виртуальным и реализовывать его в подклассе не очень хороший объектно-ориентированный дизайн, потому что подклассы не являются концептуально новыми «типами» их родительских классов, точно так же, как вам не нужно создавать подкласс кнопки изменить его высоту и ширину.
Этот тип настройки объекта абсолютно необходим для типов объектов, которые нужно иметь другое поведение, но не отличаются достаточно существенно в базовой структуре, чтобы гарантировать подкласс.
Это чаще всего появляется в элементах типа Window, которые имеют OnClick
Тип функции обратного вызова. Если мы подкласс Button
класс каждый раз, когда мы хотели разные onClick
поведение, у вас будет около 70 подклассов Button
что все ведут себя точно так же, за исключением того, что происходит, когда onclick
обратный вызов срабатывает (class OkButtonForMainWindowDialog
, class CancelButtonForMainWindowDialog
..).
class Button
{
virtual void onClick( float x, float y ) = 0 ;
} ;
class OkButtonForMainWindowDialog : public Button
{
virtual void onClick( float x, float y )
{
// Ok, so close this dialog box.. and commit the changes..
}
} ;
Вместо этого мы определяем базовый класс Button
и разрешить вложение функционального объекта, который определяет onClick
поведение.
class Button
{
function< void ( float x, float y ) > onClick ;
} ;
Button okButton ;
okButton.onClick = []( float x, float y ){
// do whatever's necessary..
} ;
Использование обратных вызовов, как это абсолютно необходимо чтобы предотвратить умножение подклассов, как это.