![]() |
Модераторы: Daevaorn |
![]() ![]() ![]() |
|
tolgamrab |
|
||||
Новичок Профиль Группа: Участник Сообщений: 11 Регистрация: 1.2.2010 Где: г. Смоленск Репутация: нет Всего: нет |
Пытаюсь понять есть ли серьезные отличия между делегированием и закрытым наследованием или этот вопрос относится к разряду религиозных. С. Мейерс высказывается в пользу делегирования, при этом единственный серьезный аргумент в пользу делегирования - "проще для понимания" (55 советов, правило 39), Александреску повсеместно использует закрытое наследование, при этом называет это элегантным решением. Я хочу попытаться выделить отличительные особенности обоих подходов, чтобы потом использовать один из них или оба, но в разных ситуациях.
Сразу отмечу, что моделировать я собираюсь отношения "реализуется посредством", чтобы избежать дискуссий на тему открытого наследования. Эта тема не про моделирование отношений "является". Попытаюсь сосредоточится на случае, когда оба класса проектирую я. То есть что нужно - пишу, что не нужно - выкидываю, ошибки исправляю. Типичный пример подобной задачи - реализация стратегии поведения класса. Немного уточню задачу. При динамическом изменении поведения объекта задумываться не о чем - классический повод для применения паттерна "стратегия". То есть делегируем абстрактному объекту функциональность, под абстрактным объектом прячется та стратегия, которая нужна в данный момент. С этим вариантом все. Если поведение объекта определено на этапе компиляции, и поведение объекта можно определить глобальной функцией, статическим методом или методом использующего класса - вопрос решается применением функтора, callback функцией или параметризацией использующего класса функцией (тот же функтор, но без объекта). С этим вариантом тоже все понятно. И все же иногда появляется пограничный случай: для каждого использующего объекта должен быть создан объект стратегии (с внутренними данными). Вот тут возможно применение и закрытого наследования и делегирования. Вопрос: что лучше? Итак отличительные особенности закрытого наследования: базовый класс проектируется с закрытым (чтобы максимально усложнить неверное использование проектируемых классов) невиртуальным (чтобы избежать лишних накладных расходов) деструктором. Для делегирования никаких ограничений нет - до него добраться снаружи не получится при всем желании. Макеты кода для этих вариантов приведены ниже. Делегирование
Для закрытого наследования:
Есть ли подводные камни? |
||||
|
|||||
Earnest |
|
|||
![]() Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Экс. модератор Сообщений: 5962 Регистрация: 17.6.2005 Где: Рязань Репутация: 53 Всего: 183 |
Немножко да. Я склоняюсь к делегированию, ибо это дает лучше разделение кода (причем включение не объекта, а ссылки). Вообще, наследование - это очень сильная связь. Наследование реализации применяю только при необходимости переопределения виртуальных функций. Представь, что через некоторое время придешь к необходимости изменять реализацию по ходу или сразу иметь несколько разных вариантов. Вариант с включением ссылки элементарно подстраивается - всего-то метод set нарисовать или что-то в этом роде. А если у тебя наследование, геморроя гораздо больше. А чем больше новых букв пишешь, тем выше вероятность ошибки, тем более глубокое тестирование необходимо... -------------------- ... |
|||
|
||||
tolgamrab |
|
|||
Новичок Профиль Группа: Участник Сообщений: 11 Регистрация: 1.2.2010 Где: г. Смоленск Репутация: нет Всего: нет |
Немножко не понял. А где же сам объект? С тесной связью согласен, один из подводных камней. При переходе к динамическому изменению стратегии проще всего будет переписать с использованием делегирования. |
|||
|
||||
mes |
|
|||
любитель ![]() ![]() ![]() ![]() Профиль Группа: Участник Клуба Сообщений: 7954 Регистрация: 14.1.2006 Репутация: 144 Всего: 250 |
поддерживаю.
Как бы это помягче сказать - имхо у Александреску несколько извращенное понятие о красоте и прозрачности кода , хотя у него можно много чему хорошему научиться. |
|||
|
||||
tolgamrab |
|
|||
Новичок Профиль Группа: Участник Сообщений: 11 Регистрация: 1.2.2010 Где: г. Смоленск Репутация: нет Всего: нет |
На вкус и цвет все кошки серые (цитата). Суть в том, что кроме как вкусом они ни тот ни другой метод ничем не мотивируют по большому счету. Я сам всегда склонялся к делегированию, но на днях пришлось просматривать код, в котором используется закрытое наследование. Стал подбирать аргументы в пользу делигирования - сходу ничего не вышло. Пересмотрел умные книжки и серьезных аргументов пока так и не нашел. |
|||
|
||||
mes |
|
|||
любитель ![]() ![]() ![]() ![]() Профиль Группа: Участник Клуба Сообщений: 7954 Регистрация: 14.1.2006 Репутация: 144 Всего: 250 |
||||
|
||||
Earnest |
|
|||
![]() Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Экс. модератор Сообщений: 5962 Регистрация: 17.6.2005 Где: Рязань Репутация: 53 Всего: 183 |
Да какая разница... например, используем умный указатель (с подсчетом ссылок) или вариант auto_ptr. По-моему, простота поддержки (и изменения) очень даже аргумент. Другое дело, что иногда бывает ясно, что оно не надо... -------------------- ... |
|||
|
||||
mes |
|
|||
любитель ![]() ![]() ![]() ![]() Профиль Группа: Участник Клуба Сообщений: 7954 Регистрация: 14.1.2006 Репутация: 144 Всего: 250 |
||||
|
||||
tolgamrab |
|
|||
Новичок Профиль Группа: Участник Сообщений: 11 Регистрация: 1.2.2010 Где: г. Смоленск Репутация: нет Всего: нет |
||||
|
||||
tolgamrab |
|
||||||||||
Новичок Профиль Группа: Участник Сообщений: 11 Регистрация: 1.2.2010 Где: г. Смоленск Репутация: нет Всего: нет |
Появились мысли на предмет обоих подходов.
Про делегирование: есть подводный камешек, который позволяет настраивать код использующего шаблона не совсем так, как планировалось. В самом начале я намекал на этот вариант, сейчас покажу подробнее. Есть у нас спроектированный шаблон, использующий стратегию:
так вот, _policy не обязательно является объектом и занимает место в памяти, что позволяет избавиться от такого минуса, как раздувание кода. Покажу на примере. Вот стандартное использование функтора:
А вот не очень ;) Был объект, стала функция.
Код компилировался в MVS2008 и MVS6.0 sp 5 без проблем. Добавлено через 2 минуты и 59 секунд Про наследование. Попробовал исследовать полученный класс на предмет дальнейшего развития. Напомню проект использующего класса (прошу прощения за ошибку в теме - исправленный фрагмент не компилировался в MVS 6.0)
А теперь представим, что потомок TClassByInheritance захочет использовать такую же стратегию. Придется немножко скорректировать TClassByInheritance (добавляем виртуальный деструктор, чтобы уж все по уму) и проектируем наследника.
Компилятор MVS 2008 предупреждает, что эта иерархия ущербна (warning C4584). Компилятор MVS 6.0 от такой иерархии тошнит (error C2584). |
||||||||||
|
|||||||||||
mes |
|
|||
любитель ![]() ![]() ![]() ![]() Профиль Группа: Участник Клуба Сообщений: 7954 Регистрация: 14.1.2006 Репутация: 144 Всего: 250 |
В контексте "раздувания кода" обычно подразумевается наполнение исходного кода ненужными деталями В Вашем случае правильно ли понимаю, что под "раздуванием кода" подразумевается "увеличенный" объем используемой (занимаемой данными) памяти ? Добавлено @ 17:11
чего то в коде не вижу отражения этого ![]() Добавлено @ 17:13 может стоит огранить до брилиантового наследования ? ![]() Это сообщение отредактировал(а) mes - 3.2.2010, 17:13 |
|||
|
||||
tolgamrab |
|
|||
Новичок Профиль Группа: Участник Сообщений: 11 Регистрация: 1.2.2010 Где: г. Смоленск Репутация: нет Всего: нет |
Нет, не только. Для описания класса нужно больше кода, чем для описания функции. На примере выше это видно. В среднем, хороший программист совершает 3 ошибки за час работы (за точность цитаты не поручусь, С. Макконел "Совершенный код") Это одна из них. ![]() Спасибо за поправку. Деструкторы писались только для того чтобы добавить перед гими слово virtual, а про само слово забыл. Смешно. ![]() Смысла нет. На такой иерархии ничего не видно. Вот если бы стратегию наследовали защищено или открыто, тогда да, целая куча граблей. Я даже больше скажу. Испытания проводил на брилиантовой иерархии + прямое наследование: количество граблей то же, только сложность увеличилась. зы. Уже после этого обсуждали проблему в коллективе и добавили несколько камней в огород закрытого наследования, потом выложу. |
|||
|
||||
tolgamrab |
|
|||
Новичок Профиль Группа: Участник Сообщений: 11 Регистрация: 1.2.2010 Где: г. Смоленск Репутация: нет Всего: нет |
Никто ничего не пишет, а жаль хотелось услышать больше мнений.
Но раз так опишу хоть выводы к которым пришел. Итак Делегирование: + 1. Простой переход к динамической стратегии + 2. Полное сокрытие реализации класса стратегии + 3. Возможность оптимизации размера класса путем использования функтора без объекта - 4. Немного более сложная работа со стратегиями имеющими закрытые или чисто виртуальные члены. В этом случае нужно создавать наследника (нормального, открытого) и делегировать уже ему. Закрытое наследование: + 1. Отличный метод для использования функциональности пустых классов (не имеющих ни данных ни функций кроме конструктора и деструктора). Для более подробной информации можно почитать Александреску + 2. Простая работа со стратегиями имеющими закрытые члены. - 3. Работа с виртуальными членами стратегии - однозначно грабли. Никто не запретит потомкам переопределить виртуальные функции, в какую секцию ее не помещай. - 4. Усложняется переход к динамической стратегии. ![]() - 5. Существенное влияние на развитие иерархии. На мой взгляд грабли - 6. Более жесткий контракт на класс стратегии: деструктор должен быть закрытым (чтобы нельзя было привести к базовому классу); не должны быть перегружены операторы new, delete, так как в этом случае на использующий класс накладываются неоправданные ограничения. Итого: для пустых классов в рамках идиомы compile-time type checking буду использовать закрытое наследование; для всех остальных случаев - делегирование, так как небольшой минус не перевешивает две пары граблей и стенку ![]() Спасибо за внимание. |
|||
|
||||
![]() ![]() ![]() |
Правила форума "С++:Общие вопросы" | |
|
Добро пожаловать!
Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, Earnest Daevaorn |
1 Пользователей читают эту тему (1 Гостей и 0 Скрытых Пользователей) | |
0 Пользователей: | |
« Предыдущая тема | C/C++: Общие вопросы | Следующая тема » |
|
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности Powered by Invision Power Board(R) 1.3 © 2003 IPS, Inc. |