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

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Закрытое наследование или делегирование? отношение "реализуется посредством" 
:(
    Опции темы
tolgamrab
Дата 1.2.2010, 14:20 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 11
Регистрация: 1.2.2010
Где: г. Смоленск

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



Пытаюсь понять есть ли серьезные отличия между делегированием и закрытым наследованием или этот вопрос относится к разряду религиозных. С. Мейерс высказывается в пользу делегирования, при этом единственный серьезный аргумент в пользу делегирования - "проще для понимания" (55 советов, правило 39), Александреску повсеместно использует закрытое наследование, при этом называет это элегантным решением. Я хочу попытаться выделить отличительные особенности обоих подходов, чтобы потом использовать один из них или оба, но в разных ситуациях.
Сразу отмечу, что моделировать я собираюсь отношения "реализуется посредством", чтобы избежать дискуссий на тему открытого наследования. Эта тема не про моделирование отношений "является".
Попытаюсь сосредоточится на случае, когда оба класса проектирую я. То есть что нужно - пишу, что не нужно - выкидываю, ошибки исправляю.
Типичный пример подобной задачи - реализация стратегии поведения класса. Немного уточню задачу.

При динамическом изменении поведения объекта задумываться не о чем - классический повод для применения паттерна "стратегия". То есть делегируем абстрактному объекту функциональность, под абстрактным объектом прячется та стратегия, которая нужна в данный момент. С этим вариантом все.
Если поведение объекта определено на этапе компиляции, и поведение объекта можно определить глобальной функцией, статическим методом или методом использующего класса - вопрос решается применением функтора, callback функцией или параметризацией использующего класса функцией (тот же функтор, но без объекта). С этим вариантом тоже все понятно.
И все же иногда появляется пограничный случай: для каждого использующего объекта должен быть создан объект стратегии (с внутренними данными). Вот тут возможно применение и закрытого наследования и делегирования. Вопрос: что лучше?

Итак отличительные особенности закрытого наследования: 
базовый класс проектируется с закрытым (чтобы максимально усложнить неверное использование проектируемых классов) невиртуальным (чтобы избежать лишних накладных расходов) деструктором.

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

Макеты кода для этих вариантов приведены ниже.
Делегирование
Код

class TResource {};
class TDelegate
{
public:
     TResource* getResource(void);
private:
     TSomeData _someData;
};
template<class TPolicy>
class TClassByDelegate
{
private: 
     TPolicy _policy;
     void someFunction(void)
     {
          auto_ptr<TResource> resource = _policy.getResource();
     }
};


Для закрытого наследования:
Код

class TResource {};
class TDelegate
{
public:
     TResource* getResource(void);
protected:
     ~TDelegate(void) {};
private:
     TSomeData _someData;
};
template<class TPolicy>
class TClassByInheritance: private TPolicy
{
private: 
     void someFunction(void)
     {
          auto_ptr<TResource> resource = TPolicy::getResource();
     }
};


Есть ли подводные камни?

PM MAIL ICQ   Вверх
Earnest
Дата 1.2.2010, 14:36 (ссылка) |    (голосов:1) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Экс. модератор
Сообщений: 5962
Регистрация: 17.6.2005
Где: Рязань

Репутация: 53
Всего: 183



Цитата(tolgamrab @  1.2.2010,  15:20 Найти цитируемый пост)
или этот вопрос относится к разряду религиозных.

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


--------------------
...
PM   Вверх
tolgamrab
Дата 1.2.2010, 14:49 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 11
Регистрация: 1.2.2010
Где: г. Смоленск

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



Цитата

причем включение не объекта, а ссылки

Немножко не понял. А где же сам объект?

С тесной связью согласен, один из подводных камней. При переходе к динамическому изменению стратегии проще всего будет переписать с использованием делегирования.
PM MAIL ICQ   Вверх
mes
Дата 1.2.2010, 16:13 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


любитель
****


Профиль
Группа: Участник Клуба
Сообщений: 7954
Регистрация: 14.1.2006

Репутация: 144
Всего: 250



Цитата(tolgamrab @  1.2.2010,  13:20 Найти цитируемый пост)
С. Мейерс высказывается в пользу делегирования

поддерживаю.

Цитата(tolgamrab @  1.2.2010,  13:20 Найти цитируемый пост)
Александреску повсеместно использует закрытое наследование, при этом называет это элегантным решением. 

Как бы это помягче сказать - имхо  у Александреску несколько извращенное понятие о красоте и прозрачности кода , хотя у него можно много чему хорошему научиться.


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


Новичок



Профиль
Группа: Участник
Сообщений: 11
Регистрация: 1.2.2010
Где: г. Смоленск

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



Цитата(mes @  1.2.2010,  16:13 Найти цитируемый пост)
у Александреску несколько извращенное понятие о красоте и прозрачности кода

На вкус и цвет все кошки серые (цитата). 

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

PM MAIL ICQ   Вверх
mes
Дата 1.2.2010, 18:05 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


любитель
****


Профиль
Группа: Участник Клуба
Сообщений: 7954
Регистрация: 14.1.2006

Репутация: 144
Всего: 250



Цитата(tolgamrab @  1.2.2010,  16:19 Найти цитируемый пост)
Суть в том, что кроме как вкусом они ни тот ни другой метод ничем не мотивируют по большому счету.

А потому что как таковых нет серьезных аргументов по этому вопросу.. Все зависит от модели восприятия. 




--------------------
PM MAIL WWW   Вверх
Earnest
Дата 2.2.2010, 13:49 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Экс. модератор
Сообщений: 5962
Регистрация: 17.6.2005
Где: Рязань

Репутация: 53
Всего: 183



Цитата(tolgamrab @  1.2.2010,  15:49 Найти цитируемый пост)
Немножко не понял. А где же сам объект?

Да какая разница... например, используем умный указатель (с подсчетом ссылок) или вариант auto_ptr.
Цитата(mes @  1.2.2010,  19:05 Найти цитируемый пост)
А потому что как таковых нет серьезных аргументов по этому вопросу.

По-моему, простота поддержки (и изменения) очень даже аргумент. Другое дело, что иногда бывает ясно, что оно не надо...


--------------------
...
PM   Вверх
mes
Дата 2.2.2010, 14:14 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


любитель
****


Профиль
Группа: Участник Клуба
Сообщений: 7954
Регистрация: 14.1.2006

Репутация: 144
Всего: 250



Цитата(Earnest @  2.2.2010,  12:49 Найти цитируемый пост)
По-моему, простота поддержки (и изменения) очень даже аргумент. Другое дело, что иногда бывает ясно, что оно не надо... 


Цитата(mes @  1.2.2010,  17:05 Найти цитируемый пост)
А потому что как таковых нет серьезных аргументов по этому вопросу.. Все зависит от модели восприятия. 

 smile 


--------------------
PM MAIL WWW   Вверх
tolgamrab
Дата 2.2.2010, 14:16 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 11
Регистрация: 1.2.2010
Где: г. Смоленск

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



Цитата(Earnest @  2.2.2010,  13:49 Найти цитируемый пост)
например, используем умный указатель (с подсчетом ссылок) или вариант auto_ptr

Совсем запутался мы об указателях или о ссылках. Можно на примере, так понятнее будет?

PM MAIL ICQ   Вверх
tolgamrab
Дата 3.2.2010, 16:22 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 11
Регистрация: 1.2.2010
Где: г. Смоленск

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



Появились мысли на предмет обоих подходов.

Про делегирование:
есть подводный камешек, который позволяет настраивать код использующего шаблона не совсем так, как планировалось. В самом начале я намекал на этот вариант, сейчас покажу подробнее.
Есть у нас спроектированный шаблон, использующий стратегию:
Код

#include <memory>
using namespace std;

class TResource {};
template<class TPolicy>
class TClassByDelegate
{
private: 
    TPolicy _policy;
public:
    void someFunction(void)
    {
        auto_ptr<TResource> resource (_policy());
    }
};


так вот, _policy не обязательно является объектом и занимает место в памяти, что позволяет избавиться от такого минуса, как раздувание кода. Покажу на примере.
Вот стандартное использование функтора:
Код

class TDelegate
{
public:
    TResource* operator()(void){ return NULL;}
private:
    TSomeData _someData;
};

void testFunction(void)
{
    TClassByDelegate<TDelegate> delegateObject;
    delegateObject.someFunction();
}


А вот не очень ;)
Был объект, стала функция. 

Код

typedef TResource* TFunction(void);
TResource* TClassByDelegate<TFunction>::_policy(void)
{
    return NULL;
}

void testFunction(void) // эта функция дополнена
{
    TClassByDelegate<TDelegate> delegateObject;
    delegateObject.someFunction();
    TClassByDelegate<TFunction> objectByFunction;
    objectByFunction.someFunction();
}


Код компилировался в MVS2008 и MVS6.0 sp 5 без проблем.

Добавлено через 2 минуты и 59 секунд
Про наследование.
Попробовал исследовать полученный класс на предмет дальнейшего развития.
Напомню проект использующего класса (прошу прощения за ошибку в теме - исправленный фрагмент не компилировался в MVS 6.0)
Код

class TSomeData {};
class TResource {};
class TBase
{
public:
     TResource* getResource(void);
protected:
     ~TBase(void) {};
private:
     TSomeData _someData;
};
template<class TPolicy>
class TClassByInheritance: private TPolicy
{
public: 
     void someFunction(void)
     {
          auto_ptr<TResource> resource( TPolicy::getResource() ); // здесь исправлено
     }
};


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

template<class TPolicy>
class TClassByInheritance: private TPolicy
{
public: 
     ~TClassByInheritance(void){};
     void someFunction(void)
     {
          auto_ptr<TResource> resource( TPolicy::getResource() );
     }
};
template<class TPolicy>
class TDerivedByInheritance: private TPolicy, public TClassByInheritance<TPolicy>
{
public: 
     ~TDerivedByInheritance(void){};
     void someOtherFunction(void)
     {
          auto_ptr<TResource> resource( TPolicy::getResource() );
     }
};


Компилятор MVS 2008 предупреждает, что эта иерархия ущербна (warning C4584).
Компилятор MVS 6.0 от такой иерархии тошнит (error C2584). 
PM MAIL ICQ   Вверх
mes
Дата 3.2.2010, 17:05 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


любитель
****


Профиль
Группа: Участник Клуба
Сообщений: 7954
Регистрация: 14.1.2006

Репутация: 144
Всего: 250



Цитата(tolgamrab @  3.2.2010,  15:22 Найти цитируемый пост)
что позволяет избавиться от такого минуса, как раздувание кода

В контексте "раздувания кода" обычно подразумевается наполнение исходного кода ненужными деталями
В Вашем случае
Цитата(tolgamrab @  3.2.2010,  15:22 Найти цитируемый пост)
 не обязательно является объектом и занимает место в памяти

 правильно ли понимаю, что под "раздуванием кода" подразумевается "увеличенный" объем используемой (занимаемой данными) памяти  ?

Добавлено @ 17:11
Цитата(tolgamrab @  3.2.2010,  15:22 Найти цитируемый пост)
Придется немножко скорректировать TClassByInheritance (добавляем виртуальный деструктор, чтобы уж все по уму)

чего то в коде не вижу отражения этого  smile

Добавлено @ 17:13
Цитата(tolgamrab @  3.2.2010,  15:22 Найти цитируемый пост)
от такой иерархии тошнит (

может стоит огранить до брилиантового наследования  ? smile


Это сообщение отредактировал(а) mes - 3.2.2010, 17:13


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


Новичок



Профиль
Группа: Участник
Сообщений: 11
Регистрация: 1.2.2010
Где: г. Смоленск

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



Цитата(mes @  3.2.2010,  17:05 Найти цитируемый пост)
правильно ли понимаю, что под "раздуванием кода" подразумевается "увеличенный" объем используемой (занимаемой данными) памяти  ?


Нет, не только. Для описания класса нужно больше кода, чем для описания функции. На примере выше это видно.


Цитата(mes @  3.2.2010,  17:05 Найти цитируемый пост)
чего то в коде не вижу отражения этого  smile

В среднем, хороший программист совершает 3 ошибки за час работы (за точность цитаты не поручусь, С. Макконел "Совершенный код")
Это одна из них.  smile 
Спасибо за поправку. Деструкторы писались только для того чтобы добавить перед гими слово virtual, а про само слово забыл. Смешно.  smile 


Цитата(mes @  3.2.2010,  17:05 Найти цитируемый пост)
может стоит огранить до брилиантового наследования  ? smile

Смысла нет. На такой иерархии ничего не видно. Вот если бы стратегию наследовали защищено или открыто, тогда да, целая куча граблей.
Я даже больше скажу. Испытания проводил на брилиантовой иерархии + прямое наследование: количество граблей то же, только сложность увеличилась. 

зы. Уже после этого обсуждали проблему в коллективе и добавили несколько камней в огород закрытого наследования, потом выложу.
PM MAIL ICQ   Вверх
tolgamrab
Дата 8.2.2010, 11:07 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 11
Регистрация: 1.2.2010
Где: г. Смоленск

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



Никто ничего не пишет, а жаль хотелось услышать больше мнений.
Но раз так опишу хоть выводы к которым пришел.
Итак
Делегирование:
+ 1. Простой переход к динамической стратегии
+ 2. Полное сокрытие реализации класса стратегии
+ 3. Возможность оптимизации размера класса путем использования функтора без объекта
- 4. Немного более сложная работа со стратегиями имеющими закрытые или чисто виртуальные члены. В этом случае нужно создавать наследника (нормального, открытого) и делегировать уже ему.

Закрытое наследование:
+ 1. Отличный метод для использования функциональности пустых классов (не имеющих ни данных ни функций кроме конструктора и деструктора). Для более подробной информации можно почитать Александреску
+ 2. Простая работа со стратегиями имеющими закрытые члены.
- 3. Работа с виртуальными членами стратегии - однозначно грабли. Никто не запретит потомкам переопределить виртуальные функции, в какую секцию ее не помещай.
- 4. Усложняется переход к динамической стратегии.  smile 
- 5. Существенное влияние на развитие иерархии. На мой взгляд грабли
- 6. Более жесткий контракт на класс стратегии: деструктор должен быть закрытым (чтобы нельзя было привести к базовому классу); не должны быть перегружены операторы new, delete, так как в этом случае на использующий класс накладываются неоправданные ограничения. 
Итого:
для пустых классов в рамках идиомы compile-time type checking буду использовать закрытое наследование;
для всех остальных случаев - делегирование, так как небольшой минус не перевешивает две пары граблей и стенку smile

Спасибо за внимание.

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

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

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

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

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


 




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


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

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