Использование обратного вызова для настройки класса вместо создания подклассов

Функционально идентичные используют обратный вызов для настройки поведения класса, а также используют виртуальную функцию и наследование. Но я считаю, что использование функциональных объектов немного более гибко в моей собственной работе.

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" ) ;
} ;

У кого-нибудь есть имя для того, что я здесь делаю или по какой-либо причине? не использовать это

0

Решение

Сказать, что это «немного более гибко», чем виртуальные функции, все равно, что сказать goto немного более гибок, чем если бы / иначе. С вашей альтернативой вы можете:

  • Изменить поведение объекта на лету (просто назначив новый объект)
  • Комбинируйте и сопоставляйте поведение в классах, которые имеют более одного из них, и, наоборот, никогда не предписывают, чтобы две связанные реализации всегда использовались вместе в таком классе.
  • По сути, сделать каждый экземпляр класса подклассом.

Однако функциональные объекты могут не получить доступ к защищенным членам «своего» объекта, что может потребовать, чтобы вы нарушили инкапсуляцию в классе хоста, чтобы объекты функции могли делать то, что им нужно.

В целом, это кажется хорошим способом связать объект с внешнее поведение это должно быть инициировано этим объектом (например, обработчиком щелчка кнопки), но использование его для расширения присущего классу поведения кажется создающим больше проблем, которые он решает.

Наиболее распространенный пример хорошего использования этой парадигмы — для функций обратного вызова или обработчиков событий, потому что класс связан с механикой рисования кнопки и обработкой событий мыши (для класса кнопки), или с управлением сетевым протоколом (для класс сетевого сокета). В этих случаях делать обработчик событий виртуальным и реализовывать его в подклассе не очень хороший объектно-ориентированный дизайн, потому что подклассы не являются концептуально новыми «типами» их родительских классов, точно так же, как вам не нужно создавать подкласс кнопки изменить его высоту и ширину.

2

Другие решения

Этот тип настройки объекта абсолютно необходим для типов объектов, которые нужно иметь другое поведение, но не отличаются достаточно существенно в базовой структуре, чтобы гарантировать подкласс.

Это чаще всего появляется в элементах типа 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..
} ;

Использование обратных вызовов, как это абсолютно необходимо чтобы предотвратить умножение подклассов, как это.

0

По вопросам рекламы ammmcru@yandex.ru
Adblock
detector