Модераторы: Daevaorn
  

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> невирт. публичные методы в базовом классе, Я vs Герб Саттер :D 
V
    Опции темы
Alek86
Дата 9.10.2007, 13:05 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1299
Регистрация: 30.1.2007
Где: Киев

Репутация: 21
Всего: 25



Вопрос для тех, кто читал книгу "Новые сложные задачи по С++" Герба Саттера.

В своей задаче №18 "Виртуальность" он привел пример правильного, по его мнению использования виртуальных функций:

Код

// пример 18-2: более современный базовый класс, 
// использующий Невиртуальный Интерфейс (NVI) 
// для отделения интерфейса от внутренней 
// реализации класса 
// 
class widget { 
public: 
    // Стабильный невиртуальный интерфейс 
    int Process( Gadget& ); // использует DoProcess...() 
    bool isDoneQ; // Использует DolsDone() 
private: 
    // настройка ~ деталь реализации, которая может как 
    // соответствовать интерфейсу, так и не соответствовать 
    // ему. каждая из этих функций может (не обязательно) 
    // быть чисто виртуальной и, если это так, иметь (или не 
    // иметь) реализацию в классе widget (см. [SutterO2]) 
    virtual int DoProcessPhasel( Gadgets ); 
    virtual int DoProcessPhase2( Gadget& ); 
    virtual bool DolsDoneO; 
    // ...
};


но я (на теперешний момент) с ним абсолютно не согласен (понимаю, заява та еще...), потому что тогда для класса Widget нельзя будет написать отакой интерфейс, к примеру, для использования в паттерне, к примеру, Decorator.

Код

class IWidget {
    virtual ~IWifdget()                 = 0{}
    virtual int Process( Gadget& )      = 0;
    virtual bool isDoneQ                = 0;
};


ведь мне тогда легко может потребоваться сделать еще один вариант Widget'a, который будет включать и делегировать класс из примера 18-2.
а если функции невиртуальные, то никакого делегирования не получится.


Это мои соображения. Но авторитет автора подсказывает мне, что неправ я. Но в чем - не пойму. Может кто-нибудь объяснить чем именно, или дать ссылку на обсужения?



--------------------
user posted image    user posted image
PM MAIL   Вверх
Fazil6
Дата 9.10.2007, 14:45 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1653
Регистрация: 3.5.2006
Где: Минск

Репутация: 35
Всего: 60



Цитата(Alek86 @  9.10.2007,  13:05 Найти цитируемый пост)
 потому что тогда для класса Widget нельзя будет написать отакой интерфейс, к примеру, для использования в паттерне, к примеру, Decorator.

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

Идея Саттера в том, что widget - это базовый класс, который полностью контролирует свой интерфейс, т.е. наследники не определяют никаких методов в секции public и тем самым любое введение или изменение, скажем, пред и пост условий делается в одном месте - в невиртуальной функции базового класса. Также, например,
Код

     int Process( Gadget& ); // использует DoProcess...() 

....

     virtual int DoProcessPhasel( Gadgets ); 
     virtual int DoProcessPhase2( Gadget& ); 

показано, что реализация DoProcess может быть разделена на некие фазы, которые могут по разному реализовываться наследниками, но такое по ведение с открытыми виртуальными функциями усложнит интерфейс, а что более важно сильно усложнит использование такого интерфейса.
                                                                                                                            
PM MAIL   Вверх
Alek86
Дата 9.10.2007, 15:27 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1299
Регистрация: 30.1.2007
Где: Киев

Репутация: 21
Всего: 25



вроде понял.
поторопился я с вопросом

спасибо.


--------------------
user posted image    user posted image
PM MAIL   Вверх
Alek86
Дата 9.10.2007, 15:49 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1299
Регистрация: 30.1.2007
Где: Киев

Репутация: 21
Всего: 25



хотя, уточню...

получается, что если мне потребуется создать класс Widget3steps, функция Process которого будет должна состоять из 3х шагов, то я этим способом не смогу никак использовать наработки из класса Widget? Ведь если я сделаю Widget3steps потомком класса Widget, то мне придется заместить функцию Process, то есть ни о каком полиморфизме не может быть и речи.

из всего этого вывод, что прибегать к такому методу нужно только в случае, если Process будет 100% вызывать 2 функции (к примеру), поскольку если все невиртуальные функции будут только делегировать виртуальные, то смысла в таком построении класса нет.


--------------------
user posted image    user posted image
PM MAIL   Вверх
Fazil6
Дата 9.10.2007, 16:25 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1653
Регистрация: 3.5.2006
Где: Минск

Репутация: 35
Всего: 60



Цитата(Alek86 @  9.10.2007,  15:49 Найти цитируемый пост)
получается, что если мне потребуется создать класс Widget3steps, функция Process которого будет должна состоять из 3х шагов, то я этим способом не смогу никак использовать наработки из класса Widget? Ведь если я сделаю Widget3steps потомком класса Widget, то мне придется заместить функцию Process, то есть ни о каком полиморфизме не может быть и речи.

все не так. ничего замещать не нужно. Что значит из 3-х шагов? вопервых шагов может быть сколько угодно и их количество определяется базовым классом, во вторых главная идея такого подхода - разделение интерфейса и реализации. Ведь ничто тебе не препятствует в потомке Widget3steps написать просто третью функцию и вызвать ее в реализации 2-го шага , а если это невозможно, то видимо имеем ошибку проектирования - ибо тогда и с открытыми виртуальными функциями будем иметь проблемы, так как вызовы для потомков видимо будут разные и полиморфно через интерфейс родителя работать будет невозможно.
Опять же если нам вдруг понадобился в каком-то одном потомке 3-й шаг, то добавляем в родителе вызов 3-й виртуальной функции, которая ничего не делает и переопределяем ее только в том классе, которому она требуется и на интерфейсе и полиморфном использовании нашей иерархии это никак не скажется.
Цитата(Alek86 @  9.10.2007,  15:49 Найти цитируемый пост)
из всего этого вывод, что прибегать к такому методу нужно только в случае, если Process будет 100% вызывать 2 функции (к примеру), поскольку если все невиртуальные функции будут только делегировать виртуальные, то смысла в таком построении класса нет.

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

PM MAIL   Вверх
Alek86
Дата 9.10.2007, 17:33 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1299
Регистрация: 30.1.2007
Где: Киев

Репутация: 21
Всего: 25



хорошие объяснения, спасибо.
но еще непонятка:

Все-таки не очень смог представить, как реализовать декоратор
ведь для него требуется, чтобы были:
1. интерфейс - он у нас есть

2. возможность наследникам интерфейса включать "экземпляр" этого-же самого интерфейса и делегировать его функции.
А вот тут проблемы, так как я не смогу сделегировать никакие виртуальные функции (которые, по сути, и будут "рабочей" частью класса). Вызвать публичные функции (невиртуальные) я смогу, но где?!

Цитата(Fazil6 @  9.10.2007,  16:25 Найти цитируемый пост)
главная идея такого подхода - разделение интерфейса и реализации

Но почему именно невиртуальными функциями? Чтоб поставить "заглушку"? Ведь если будет все точно также, но публичные будут виртуальными, то проблем с включением не будет.


--------------------
user posted image    user posted image
PM MAIL   Вверх
Fazil6
Дата 9.10.2007, 18:14 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1653
Регистрация: 3.5.2006
Где: Минск

Репутация: 35
Всего: 60



Цитата(Alek86 @  9.10.2007,  17:33 Найти цитируемый пост)
2. возможность наследникам интерфейса включать "экземпляр" этого-же самого интерфейса и делегировать его функции.А вот тут проблемы, так как я не смогу сделегировать никакие виртуальные функции (которые, по сути, и будут "рабочей" частью класса). Вызвать публичные функции (невиртуальные) я смогу, но где?!

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

Код

class widget { 
public: 
    
    int Process( Gadget& G ) { return DoProcessPhasel( G );}
    bool isDoneQ(){return DolsDoneO();} 
private: 
   
    virtual int DoProcessPhasel(  Gadget& G  ); 
   
    virtual bool DolsDoneO(); 
    // ...
}; 

class widgetDecorator
{
public:
     widgetDecorator( widget *w) :wid(w){}
private:
     widget *wid;
     
     virtual int DoProcessPhasel(  Gadget& G  )
     {
          
           wid->Process(G);  
          // добавляем что надо
     } 
 }


Добавлено @ 18:22
Цитата(Alek86 @  9.10.2007,  17:33 Найти цитируемый пост)
Но почему именно невиртуальными функциями? Чтоб поставить "заглушку"? Ведь если будет все точно также, но публичные будут виртуальными, то проблем с включением не будет.

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


Это сообщение отредактировал(а) Fazil6 - 9.10.2007, 18:34
PM MAIL   Вверх
Alek86
Дата 9.10.2007, 19:36 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1299
Регистрация: 30.1.2007
Где: Киев

Репутация: 21
Всего: 25



и, если Process( Gadget& G ) вызывает 2 виртуальные функции DoProcessPhasel( Gadgets ) и DoProcessPhase2( Gadget& ), то вызывать  wid->Process(G) (из твоего примера) в первой из них?

да, тогда проблем особо нет, если в Process( Gadget& G ) очень простая логика, без if'ов.


под термином "включение" я подразумевал ту самую альтенативу наследованию, которую все, кому не лень, рекомендуют использовать в большинстве случаев (и не зря). Забыл, как оно правильно зовется smile


--------------------
user posted image    user posted image
PM MAIL   Вверх
Fazil6
Дата 9.10.2007, 21:50 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1653
Регистрация: 3.5.2006
Где: Минск

Репутация: 35
Всего: 60



Цитата(Alek86 @  9.10.2007,  19:36 Найти цитируемый пост)
и, если Process( Gadget& G ) вызывает 2 виртуальные функции DoProcessPhasel( Gadgets ) и DoProcessPhase2( Gadget& ), то вызывать  wid->Process(G) (из твоего примера) в первой из них?

а какая разница? Мы ведь говорим о конкретном паттерне Decorator, а его смысл это передать вызов метода интерфейса реальному экземпляру и добавить что-то еще свое. Т.е. даже если рассматривать в качестве примера код из Саттера с вызовами 2-х шагов, то для декоратора здесь по барабану эта логика. По логике работы интерфейса widget и логике паттерна отработают виртуальные функции экземпляра, который декоратор хранит, а в какой из виртуальных функций декоратора его теребить, тут уж от конкретной задачи зависит... 

Цитата(Alek86 @  9.10.2007,  19:36 Найти цитируемый пост)
да, тогда проблем особо нет, если в Process( Gadget& G ) очень простая логика, без if'ов.

по своей сути невиртуальный интерфейс просто добавляет еще один вызов функции без добавления бизнеса и поэтому сами функции в интерфейсе класса обычно очень простые. Как правило там просто вызов непаблик виртуальной функции и все.
Цитата(Alek86 @  9.10.2007,  19:36 Найти цитируемый пост)
под термином "включение" я подразумевал ту самую альтенативу наследованию, которую все, кому не лень, рекомендуют использовать в большинстве случаев (и не зря). Забыл, как оно правильно зовется 

а, ты про композицию... Мне становится ясна твоя мысль. 
Цитата(Alek86 @  9.10.2007,  17:33 Найти цитируемый пост)
2. возможность наследникам интерфейса включать "экземпляр" этого-же самого интерфейса и делегировать его функции.А вот тут проблемы, так как я не смогу сделегировать никакие виртуальные функции (которые, по сути, и будут "рабочей" частью класса). Вызвать публичные функции (невиртуальные) я смогу, но где?!

Только не совсем понимаю почему ты решил, что ты должен делегировать именно виртуальные функции при композиции вместо наследования. Делегировать нужно функции открытого интерфейса , а виртуальные они или нет , кого это колышет? Какая тебе разница когда ты вызываешь функцию у объекта виртуальная она или нет?
PM MAIL   Вверх
Alek86
Дата 9.10.2007, 22:09 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1299
Регистрация: 30.1.2007
Где: Киев

Репутация: 21
Всего: 25



да уж, тут у меня прокол.
и хотя уж очень не нравится, что нужно "засорять" DoProcessPhasel( Gadgets ) в начале вызовом wid->Process(G), зато, вроде, все стало на свои места...

спасибо, щас плюсану smile

Добавлено через 1 минуту и 35 секунд
думаю, вопрос решен


--------------------
user posted image    user posted image
PM MAIL   Вверх
  
Ответ в темуСоздание новой темы Создание опроса
Правила форума "С++:Общие вопросы"
Earnest Daevaorn

Добро пожаловать!

  • Черновик стандарта C++ (за октябрь 2005) можно скачать с этого сайта. Прямая ссылка на файл черновика(4.4мб).
  • Черновик стандарта C (за сентябрь 2005) можно скачать с этого сайта. Прямая ссылка на файл черновика (3.4мб).
  • Прежде чем задать вопрос, прочтите это и/или это!
  • Здесь хранится весь мировой запас ссылок на документы, связанные с C++ :)
  • Не брезгуйте пользоваться тегами [code=cpp][/code].
  • Пожалуйста, не просите написать за вас программы в этом разделе - для этого существует "Центр Помощи".
  • C++ FAQ

Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, Earnest Daevaorn

 
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | C/C++: Общие вопросы | Следующая тема »


 




[ Время генерации скрипта: 0.0550 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.