Добрый день,
я все-таки никак не пойму почему у "профессиональных" разработчиков ПО часто прослеживается паранойя наследования — надо что-то заимплементить, мы пронаследуемся и еще раз и еще.... Может стоит остановиться и подумать над дизайном??? Ведь неоправданное наследование это тупиковый вариант, для последующего внесения изменений, они нарастают как снежный ком...
10.04.08 11:33: Перенесено модератором из 'C/C++' — Odi$$ey
Здравствуйте, antonlavreev, Вы писали:
A>Да у меня как раз нет просто во многих случаях я не понимаю зачем городить огород с наследованием, если можно использовать например, decorator или моделирование на основе стратегий и т.д.
Предложите программистам почитать, к примеру, GoF-ов. Наследованию стоит предпочитать композицию объектов.Наследование
Плюсы:
статически детерминировано;проще в использовании, так как вшито в ООЯ.
Минусы:
статическая определенность кроме того и минус: нельзя изменить реализацию во время исполнения;наследование нарушает инкапсуляцию, так как наследник имеет доступ к членам класса родителя, как следствие, в наследниках возникают, к примеру, проблемы безопасности инициализации при потере указателя this, вызов виртуальных методов, потоков и т.п. из конструктора и прочее;наследование настолько тесно связывает классы, что изменение в родителе вызывает или волну изменений в наследниках, или волну ошибок в оных.
Композиция
Плюсы
определяется динамически за счет перекрестных ссылок (не связаны руки компилятором, во время исполнения любой объект может быть подменен другим соответствующего типа);применяется на основе взаимного соблюдения классами контрактов друг друга, предпочтительно основанных на интерфейсах, то есть не нарушается принцип инкапсуляции;не помню, писали об этом GoF-ы или нет: такие структуры объектов проще понимать, чем сложные графы наследования, ведь композиция приводит к более простым графам наследования, принуждает постепенно писать ориентируясь на шаблоны проектирования, в первую же очередь на основной принцип — одной ответственности класса.
Минусы:
на ум приходит только высокий порог вхождения, то есть необходимость в некотором минимальном опыте разработки, чтобы понять, чем композиция лучше наследования; соответственно минус в том, что новичкам сложно понимать "композиционный код".
Многие задачи, решаемые наследованием, могут быть решены, как вы и сказали выше, на основе делегирования (а это и есть один из вариантов композиции). Кроме того, часто от наследования можно уйти за счет обобщенного программирования.
Здравствуйте, antonlavreev, Вы писали:
A>Добрый день, A> я все-таки никак не пойму почему у "профессиональных" разработчиков ПО часто прослеживается паранойя наследования — надо что-то заимплементить, мы пронаследуемся и еще раз и еще.... Может стоит остановиться и подумать над дизайном??? Ведь неоправданное наследование это тупиковый вариант, для последующего внесения изменений, они нарастают как снежный ком...
Да, иной раз смотришь на то, что понаписали джуниоры, и понимаешь, что порой наследование — это как копипаст, но без копипаста
Здравствуйте, WFrag, Вы писали:
WF>Для той же Java это в общем случае не верно. При компиляции у тебя может быть один SomeSuperClass класс, а при выполнении окажется совсем другой. Более того, он даже по методам/полям может быть местами не совместим.
Совсем не понял, разжуйте.
WF>Тут тоже не всё так просто. Очень часто композиция фиксируется однажды (например, IoC контейнером при запуске приложения) и не меняется во время работы.
Фиксация времени выполнения — это выполнение процессором некоторого набора команд. Фиксация времени компилирования — это фиксация состава этого набора команд. Разницу совсем не видите, да?
Plague,
I>>IMHO стоит, если наследование неприватное.
P>По моему уже действительно паранойя...
P>Сначала, большие возражения против множественного наследования. Да, это очень острый скальпель, и порезаться как нефиг-нафиг. А теперь и просто наследование приселдуется... мне кажется, это происки поклонников структурного программирования! Всемирный заговор! =))
Наследование классов (в т.ч. и множественное) полностью мажорируется комбой наследование_интерфейсов-реализация (а реализация соответственно — через композицию), так как не подвержена проблеме сильного связывания между классом и подклассом (и "diamond problem" в случае множественного наследования). А если эту комбу навернуть до трейтов-миксинов, то мажорируется в плане удобства программирования тоже.
P>Наследование более логично, чем композиция, когда есть отношение между объектами "is a". По-сути, при отсутствии реализации наследования его "эмуляли" композицией.
Сегодня isa, а завтра уже isnota. Или там вставить доп. класс между родителем и потомком (пирожок уже подрумянился, но теперь нужно просверлить дырку и затолкать мясо). Реалии жызни-с...
Здравствуйте, vdimas, Вы писали:
V>Композиции — это хорошо, и стратегии мне представляются одним из самых удобных и гибких способов композиции, жаль, что язык C# не очень удобен для применения этого паттерна, из-за отсутствия доступа к статическим членам генериков в дизайне и невозможности указать требование для генерика конструктора определённой сигнатуры. Всё решаемо, конечно, буквально одним-двумя лишними методами в контрактах, но это портит красивую картинку самого метода декомпозиции. Правда, и с этим борятся, через аттрибуты, рефлекшен, а вон в новом WPF — и через XML, повышая долю декларативного при использовании этого подхода.
Честно говоря, не понял суть проблемы и весь героизм ее преодоления.
V>Интересно, что сами стратегии на практике удобно реализовывать именно как некое дерево наследования, но при этом монстроидальное "общее" дерево наследования системы классов может распасться на малосвязанные небольшие деревца.
У этих "деревцев" есть вполне себе официальное название — каркасы. И это опять же композиция, за счет повторного использования которой достигается повторное использование не просто кода, а дизайна кода; подробнее описывал здесь: Каркасы и шаблоны проектирования
.
V>В самом наследовании реализации никакого криминала нет, скорее — наоборот, просто текущие негативные высказывания относительно наследования реализации существуют из-за потери чувства меры при проектировании системы типов, где, в результате построения ветвистых деревьев наследования, мы получаем не упрощение, а усложнение структуры.
Никто не утверждал, что наследованием следует полностью прекратить пользоваться, речь шла только о том, что чем более опытным становится проектировщик, тем больше в его коде композиции объектов и тем меньше наследования. А основной негатив относится к применению нубами наследования даже в случае отсутствия между базовым и дочерним классами отношения "является". Выше уже кажется говорили об этом: унаследовавшись от объекта, вместо его агрегирования, разработчик дает любому возможность использования функциональности родителя напрямую, ну со всеми втекающими-вытекающими из этого последствиями.
Здравствуйте, antonlavreev, Вы писали:
A>Добрый день, A> я все-таки никак не пойму почему у "профессиональных" разработчиков ПО часто прослеживается паранойя наследования — надо что-то заимплементить, мы пронаследуемся и еще раз и еще.... Может стоит остановиться и подумать над дизайном??? Ведь неоправданное наследование это тупиковый вариант, для последующего внесения изменений, они нарастают как снежный ком...
А можно как-то более конкретно пояснить, о каких "профессиональных" разработчиках идет речь и привести примеры случаях неуместного наследования, сделанного ими? В моем представлении если разработчик применяет наследодование без рабору, где нужно и где не нужно, то профессиональным его назвать никак нельзя.
... << RSDN@Home 1.2.0 alpha rev. 787>>
--
Справедливость выше закона. А человечность выше справедливости.
Здравствуйте, antonlavreev, Вы писали:
A>Добрый день, A> я все-таки никак не пойму почему у "профессиональных" разработчиков ПО часто прослеживается паранойя наследования — надо что-то заимплементить, мы пронаследуемся и еще раз и еще.... Может стоит остановиться и подумать над дизайном??? Ведь неоправданное наследование это тупиковый вариант, для последующего внесения изменений, они нарастают как снежный ком...
Здравствуйте, antonlavreev, Вы писали:
A>Добрый день, A> я все-таки никак не пойму почему у "профессиональных" разработчиков ПО часто прослеживается паранойя наследования — надо что-то заимплементить, мы пронаследуемся и еще раз и еще.... Может стоит остановиться и подумать над дизайном??? Ведь неоправданное наследование это тупиковый вариант, для последующего внесения изменений, они нарастают как снежный ком...
Встает вопрос, а нужно ли использовать наследование вообще?
Отношение "интерфейс-реализация" — согласен, нужно и необходимо на уровне языка программирования.
А вот моделировать отношение "широкое понятие — узкое понятие" теми способами, которые предлагают C++/Java (наследование) — куча смутных сомнений.
Как считает цивилизованная общественность, идея полного отказа от наследования в пользу делегирования — имеет ли право на существование?
Здравствуйте, igna, Вы писали:
R>>... статически детерминировано; I>Это что?
Наследование класса определяется статически на этапе компиляции кода, в отличие от композиции, которая определяется только во время исполнения откомпилированного кода.
I>http://en.wikipedia.org/wiki/Statically_indeterminate?
Очень смешно.
Здравствуйте, igna, Вы писали:
I>Слово "только" здесь лишнее. Фразу "Object composition is defined dynamically at run-time through objects acquiring references to other objects" (GoF) следует понимать как "Динамически композиция объектов определяется посредством получения ссылок на другие объекты". Что вовсе не означает будто композиция не может определяться статически.
Означает. При наследовании время жизни предка и потомка совпадают, соответственно, когда мы уже в контексте обсуждения возможностей потомка (рассматриваем время строго после завершения конструктора) автоматически подразумевается, что родитель тоже существует — это и есть фиксация (есть один объект <=> есть другой объект). А при композиции время жизни объектов не совпадает, в общем случае, в особенности если забыть о GC, вообще никак не связано, а значит, что пока один объект (уже существующий) не получит ссылку на другой объект, он (первый) не может сделать никаких предположений о существовании или нет второго — он попросту не имеет к тому никаких возможностей. Таким образом, говорить здесь какой-то "статической фиксации" является словоблудием.
Здравствуйте, Plague, Вы писали:
P>При этом статическая особенность наследования относительно композиции, подобно статической и динамической типизации, и для С++ является более логичным.
Композиция может быть статической.
Если статическая композиция достаточна, не стоит использовать наследование только для того, чтобы сэкономить на написании forwarding functions.
Здравствуйте, antonlavreev, Вы писали:
A> я все-таки никак не пойму почему у "профессиональных" разработчиков ПО часто прослеживается паранойя наследования — надо что-то заимплементить, мы пронаследуемся и еще раз и еще.... Может стоит остановиться и подумать над дизайном??? Ведь неоправданное наследование это тупиковый вариант, для последующего внесения изменений, они нарастают как снежный ком...
A>> я все-таки никак не пойму почему у "профессиональных" разработчиков ПО часто прослеживается паранойя наследования
J>ты параною с манией не путаешь случайно?
Путает, вне всякого сомнения. "Паранойя чего-либо" -- так по-русски вообще не говорят.
Здравствуйте, antonlavreev, Вы писали:
A>К примеру, есть некий класс B, который хранит некие идентификаторы — контейнер с числами. Далее требуется сделать так, чтобы он хранил контейнер с идентификаторами + каждому идентификатору ставился в соответствие еще один атрибут At. Происходит наследование от B. Далее другой атрибут At2 — еще раз наследование от B. Уже выглядит странновато. А затем выясняется, что нужен такой объект у которого был бы и идентификатор, и At, и At2... И лучше бы обойтись без виртуального наследования...Т.е. на каждый дополнительный атрибут(ы) будут создаваться классы-наследники...
имхо наследование нужно, когда нужно добавить/изменить поведение объекта, а не хранимые данные..
если только данные, то рефакторинг в сторону большей гибкости..
Здравствуйте, Sinclair, Вы писали:
S>Надо полагать, имелась в виду динамическая композиция:
Надо, больше не остается ничего. Но с тогда этот плюс наследования по сравнению с композицией отпадает. Используй статическую или там "статически детерминированную" композицию, вот и все. Единственный остающийся плюс это то, что наследование "проще в использовании, так как вшито в ООЯ". Тут он прав, наследование "вшили" очень хорошо, и не только в ООЯ.
Здравствуйте, rsn81, Вы писали:
R>Не знаю, быть может, такой беглый пример поможет понять
Java к счастью не единственный язык. Возможно в Java наследование и в самом деле имеет тот плюс по сравнению с композицией, что наследование "статически детерминировано", но в других языках "статически детерминированной" может быть и композиция.
Здравствуйте, Plague, Вы писали:
P>это очень предвзято... сказать, что композиция хоть как-то упрощает структуру кода.. не думаю...наследование может быть приватным... =)
Против приватного наследования возражать не стану.
P>Аналогично: не стоит использовать композицию ради того, чтоб избавится от наследования?
Здравствуйте, igna, Вы писали:
I>IMHO стоит, если наследование неприватное.
По моему уже действительно паранойя...
Сначала, большие возражения против множественного наследования. Да, это очень острый скальпель, и порезаться как нефиг-нафиг. А теперь и просто наследование приселдуется... мне кажется, это происки поклонников структурного программирования! Всемирный заговор! =))
Наследование более логично, чем композиция, когда есть отношение между объектами "is a". По-сути, при отсутствии реализации наследования его "эмуляли" композицией.
Здравствуйте, antonlavreev, Вы писали:
A>Добрый день, A> я все-таки никак не пойму почему у "профессиональных" разработчиков ПО часто прослеживается паранойя наследования — надо что-то заимплементить, мы пронаследуемся и еще раз и еще.... Может стоит остановиться и подумать над дизайном??? Ведь неоправданное наследование это тупиковый вариант, для последующего внесения изменений, они нарастают как снежный ком...
Кажется, "паранойя наследования" как раз у автора поста а не наоборот
"To protect people you must slay people. To let people live you must let people die. This is the true teaching of the sword."
-Seijuro Hiko, "Rurouni Kensin"
Здравствуйте, Zigmar, Вы писали:
Z>Здравствуйте, antonlavreev, Вы писали:
A>>Добрый день, A>> я все-таки никак не пойму почему у "профессиональных" разработчиков ПО часто прослеживается паранойя наследования — надо что-то заимплементить, мы пронаследуемся и еще раз и еще.... Может стоит остановиться и подумать над дизайном??? Ведь неоправданное наследование это тупиковый вариант, для последующего внесения изменений, они нарастают как снежный ком... Z>Кажется, "паранойя наследования" как раз у автора поста а не наоборот
Да у меня как раз нет просто во многих случаях я не понимаю зачем городить огород с наследованием, если можно использовать например, decorator или моделирование на основе стратегий и т.д.
Здравствуйте, Zigmar, Вы писали:
Z>Кажется, "паранойя наследования" как раз у автора поста а не наоборот
нет, это "инХЕРофобия" — боязнь наследования..
...coding for chaos...
Re: паранойя наследования
От:
Аноним
Дата:
09.04.08 18:02
Оценка:
A> я все-таки никак не пойму почему у "профессиональных" разработчиков ПО часто прослеживается паранойя наследования — надо что-то заимплементить, мы пронаследуемся и еще раз и еще.... Может стоит остановиться и подумать над дизайном??? Ведь неоправданное наследование это тупиковый вариант, для последующего внесения изменений, они нарастают как снежный ком...
Да нет ниче тупикового при грамотном использовании. Хотя конечно имеются проблемы у людей испорченных общением с делфи.
Re[3]: паранойя наследования
От:
Аноним
Дата:
09.04.08 18:31
Оценка:
Здравствуйте, antonlavreev, Вы писали:
A>Да у меня как раз нет просто во многих случаях я не понимаю зачем городить огород с наследованием, если можно использовать например, decorator или моделирование на основе стратегий и т.д.
У меня уже длительное время происходит накопление паранойи стратегий и чувствую, скоро она случится
class СChudoStrategnyiClass
: public strats::armer<СChudoStrategnyiClass, bmpl::vector<
strats::control_handleEventsStatic
,strats::control_data4UpdateCookieOnce
,strats::method
,strats::processor_static
,strats::methodInitializer_holder
,strats::singletonSeparator_fld
> >::type
{
public:
...
Здравствуйте, rg45, Вы писали:
R>Здравствуйте, antonlavreev, Вы писали:
A>>Добрый день, A>> я все-таки никак не пойму почему у "профессиональных" разработчиков ПО часто прослеживается паранойя наследования — надо что-то заимплементить, мы пронаследуемся и еще раз и еще.... Может стоит остановиться и подумать над дизайном??? Ведь неоправданное наследование это тупиковый вариант, для последующего внесения изменений, они нарастают как снежный ком...
R>А можно как-то более конкретно пояснить, о каких "профессиональных" разработчиках идет речь и привести примеры случаях неуместного наследования, сделанного ими? В моем представлении если разработчик применяет наследодование без рабору, где нужно и где не нужно, то профессиональным его назвать никак нельзя.
К примеру, есть некий класс B, который хранит некие идентификаторы — контейнер с числами. Далее требуется сделать так, чтобы он хранил контейнер с идентификаторами + каждому идентификатору ставился в соответствие еще один атрибут At. Происходит наследование от B. Далее другой атрибут At2 — еще раз наследование от B. Уже выглядит странновато. А затем выясняется, что нужен такой объект у которого был бы и идентификатор, и At, и At2... И лучше бы обойтись без виртуального наследования...Т.е. на каждый дополнительный атрибут(ы) будут создаваться классы-наследники...
Здравствуйте, antonlavreev, Вы писали:
A> я все-таки никак не пойму почему у "профессиональных" разработчиков ПО часто прослеживается паранойя наследования — надо что-то заимплементить, мы пронаследуемся и еще раз и еще.... Может стоит остановиться и подумать над дизайном??? Ведь неоправданное наследование это тупиковый вариант, для последующего внесения изменений, они нарастают как снежный ком...
ИМХО, всё дело в виде наследования: нельзя забывать, что в С++ _по умолчанию_ предлагается private-наследование, в котором ничего плохого нет: при желании заменить его на агрегирование или на что-то другое или выкинуть вовсе будет не так сложно. Проблемы есть с "видимым наружу" наследованием, которое надо обдумывать весьма и весьма тщательно.
Как говорили некогда гиганты мысли: "наследование (от себя добавлю, что речь, бузусловно, шла не о закрытом наследовании) — сильнейшая связь между объектами, разорвать которую сложнее всего". Вот поэтому избегать его, при возможности, необходимо.
... << RSDN@Home 1 alpha 3 rev. 0>>
Help will always be given at Hogwarts to those who ask for it.
Здравствуйте, rsn81, Вы писали:
>Многие задачи, решаемые наследованием, могут быть решены, как вы и сказали выше, на основе делегирования (а это и есть один из вариантов композиции). Кроме того, часто от наследования можно уйти за счет обобщенного программирования.
+1. Только старые компиляторы не позволяют иногда, а иногда и наоборот бывает приходится эмулировать наследованием отсутствие частичных специализаций.
Извиняюсь за оффтоп — накипело от общения с VC 6.
Побеждающий других — силен,
Побеждающий себя — Могущественен.
Лао Цзы
Здравствуйте, rg45, Вы писали:
R>А можно как-то более конкретно пояснить, о каких "профессиональных" разработчиках идет речь и привести примеры случаях неуместного наследования, сделанного ими? В моем представлении если разработчик применяет наследодование без рабору, где нужно и где не нужно, то профессиональным его назвать никак нельзя.
Насчет профессионалов не знаю, но всякие учебники по ООП пестрят подобными примерами. Я конечно понимаю, что их цель просто продемонстрировать наследование. Он ведь будущий профессионал должен уметь не наследовать, а программировать. Вроде так.
Здравствуйте, rg45, Вы писали:
R>А можно как-то более конкретно пояснить, о каких "профессиональных" разработчиках идет речь и привести примеры случаях неуместного наследования, сделанного ими? В моем представлении если разработчик применяет наследодование без рабору, где нужно и где не нужно, то профессиональным его назвать никак нельзя.
Классический пример в мире Java — класс Stack (или Queue, точно уж не помню) реализованный через наследование от класса Vector. Бага допущена очень давно, сейчас применяются совсем другие решения — но как пример сойдет.
Здравствуйте, WFrag, Вы писали:
WF>Здравствуйте, antonlavreev, Вы писали:
A>>Добрый день, A>> я все-таки никак не пойму почему у "профессиональных" разработчиков ПО часто прослеживается паранойя наследования — надо что-то заимплементить, мы пронаследуемся и еще раз и еще.... Может стоит остановиться и подумать над дизайном??? Ведь неоправданное наследование это тупиковый вариант, для последующего внесения изменений, они нарастают как снежный ком...
WF>Да, иной раз смотришь на то, что понаписали джуниоры, и понимаешь, что порой наследование — это как копипаст, но без копипаста
[skipped] ЖМ>Как считает цивилизованная общественность, идея полного отказа от наследования в пользу делегирования — имеет ли право на существование?
Имеет, только это крайне непрактично. Во-первых, если вы бездумно замените вертикальный граф наследования на горизональный граф композиции объектов, система станет абсолютно нечитаемой начинающими программистами. Во-вторых, это, конечно, мизер, да и не столь важно по сравнению с гибкостью системы, тем не менее будет падение производительности системы.
Здравствуйте, rsn81, Вы писали:
R>Наследование класса определяется статически на этапе компиляции кода, в отличие от композиции, которая определяется только во время исполнения откомпилированного кода.
В Java по умолчанию все вызовы виртуальные. Разве что при композиции вызов будет скорее всего через интерфейс, что медленнее просто виртуального вызова. Но в обоих случаях Hotspot может соптимизировать виртуальный вызов обычным, если посчитает нужным.
Здравствуйте, rsn81, Вы писали:
R>Наследование класса определяется статически на этапе компиляции кода, в отличие от композиции, которая определяется только во время исполнения откомпилированного кода.
Это утверждение и для C++ верно? (Хочу понять, что имеется ввиду.)
Вот композиция:
class A
{
};
class B
{
A a;
};
Та ли это композиция, "которая определяется только во время исполнения откомпилированного кода"?
Здравствуйте, igna, Вы писали:
I>Та ли это композиция, "которая определяется только во время исполнения откомпилированного кода"?
Надо полагать, имелась в виду динамическая композиция:
class B
{
A* _a;
public: B(A* a) { _a = a; }
}
Хотя скорее "полудинамическая":
class B
{
A& _a;
public: B(A& a): _a(a){}
}
... << RSDN@Home 1.2.0 alpha rev. 677>>
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, igna, Вы писали:
I>Надо, больше не остается ничего. Но с тогда этот плюс наследования по сравнению с композицией отпадает.
Дабы не гадать далее, вы вместо чтения Wiki-заборов откройте уже книгу GoF, а конкретно в главе "Механизмы повторного использования" два параграфа: "Наследование и композиция" и "Сравнение структур времени выполнения и времени компиляции".
I>Используй статическую или там "статически детерминированную" композицию, вот и все.
Вы пока не догоняете, что есть композиция, а потому сказали какой-то бред, уж извините.
I>Единственный остающийся плюс это то, что наследование "проще в использовании, так как вшито в ООЯ". Тут он прав, наследование "вшили" очень хорошо, и не только в ООЯ.
Попытка №2. Есть две абсолютно (!) различных структуры в ООП:
времени компиляции: фиксируется на этапе компиляции, так отношение наследования между классами неизменно;
времени выполнения: представляет собой постоянно меняющийся граф взаимодействующих объектов во время исполнения, так отношение владения ссылкой на объект (отношения агрегирования или осведомленности) определяется в динамике и по определению может быть изменено.
Фишка в том, что за первую структуру обычно отвечает язык (компилятор), а вот за вторую всецело проектировщик (в управляемых платформах компилятор, конечно, пытается и даже некоторые нехорошие вещи, которые могут случится во время исполнения, предсказывает, но все же компиляторы пока до телепатических способностей проектировщиков не доросли) — чем более он опытный, тем более лаконичная структура объектов первого вида, а потому чуть меньше работы компилятору, тем более сложная вторая структура, то есть мозги работают на полную.
Здравствуйте, rsn81, Вы писали:
R>Попытка №2. Есть две абсолютно (!) различных структуры в ООП:
R>времени компиляции: фиксируется на этапе компиляции, так отношение наследования между классами неизменно;
Для той же Java это в общем случае не верно. При компиляции у тебя может быть один SomeSuperClass класс, а при выполнении окажется совсем другой. Более того, он даже по методам/полям может быть местами не совместим.
R>времени выполнения: представляет собой постоянно меняющийся граф взаимодействующих объектов во время исполнения, так отношение владения ссылкой на объект (отношения агрегирования или осведомленности) определяется в динамике и по определению может быть изменено.Фишка в том, что за первую структуру обычно отвечает язык (компилятор), а вот за вторую всецело проектировщик (в управляемых платформах компилятор, конечно, пытается и даже некоторые нехорошие вещи, которые могут случится во время исполнения, предсказывает, но все же компиляторы пока до телепатических способностей проектировщиков не доросли) — чем более он опытный, тем более лаконичная структура объектов первого вида, а потому чуть меньше работы компилятору, тем более сложная вторая структура, то есть мозги работают на полную.
Тут тоже не всё так просто. Очень часто композиция фиксируется однажды (например, IoC контейнером при запуске приложения) и не меняется во время работы.
Здравствуйте, rsn81, Вы писали:
R>Наследование класса определяется статически на этапе компиляции кода, в отличие от композиции, которая определяется только во время исполнения откомпилированного кода.
Слово "только" здесь лишнее. Фразу "Object composition is defined dynamically at run-time through objects acquiring references to other objects" (GoF) следует понимать как "Динамически композиция объектов определяется посредством получения ссылок на другие объекты". Что вовсе не означает будто композиция не может определяться статически.
Здравствуйте, rsn81, Вы писали:
R>А при композиции время жизни объектов не совпадает, в общем случае, в особенности если забыть о GC, вообще никак не связано, а значит, что пока один объект (уже существующий) не получит ссылку на другой объект, он (первый) не может сделать никаких предположений о существовании или нет второго — он попросту не имеет к тому никаких возможностей. Таким образом, говорить здесь какой-то "статической фиксации" является словоблудием.
[skipped] I>Это композиция или нет?
Перечитайте определение композиции (особенно внимательно — концовку), которые вы сами же и процитировали здесь: Re[10]: паранойя наследования
Здравствуйте, rsn81, Вы писали:
R>Перечитайте определение композиции (особенно внимательно — концовку), которые вы сами же и процитировали здесь: Re[10]: паранойя наследования
Здравствуйте, antonlavreev, Вы писали:
A>Добрый день, A> я все-таки никак не пойму почему у "профессиональных" разработчиков ПО часто прослеживается паранойя наследования — надо что-то заимплементить, мы пронаследуемся и еще раз и еще.... Может стоит остановиться и подумать над дизайном??? Ведь неоправданное наследование это тупиковый вариант, для последующего внесения изменений, они нарастают как снежный ком...
Ну почему... У меня вот обратная картина: привык мыслить функционально... ОО-решение приходит только во вторую очередь, да и то, хорошенько подумать нужно...
Вот писал интерпретатор виртуальной машины... Сходу написал все на функциях... Основной цикл процессора вызывает нужную функцию по указателю из массива указателей. А код операции является индексом.
А потом только подумал, что можно было определить абстрактный класс Команда и наследоваться от него для реализации каждой команды.
Пока реализовал промежуточное решение: разделил все на 2 дружественных класса: класс-процессор и класс-система команд. Инкапсулировал все функции-команды в класс система-команд как статические. Дальше буду реализовывать нормальный ОО-подход.
Это я опять к топику "что плохого в С++". Сильно много свободы. Одну и ту же работу — в трех разных видах делаю. Правда плюс есть — можно сравнивать подходы на практике. Но это только мне как преподу и пригодиться. А в реальном программировании сравнивать некогда — "копать" надо...
Вот и получается, что в большом проекте каждый пишет как привык, а не так, как нужно...
Хочешь быть счастливым — будь им!
Без булдырабыз!!!
Здравствуйте, rsn81, Вы писали:
R>Здравствуйте, WFrag, Вы писали:
WF>>Для той же Java это в общем случае не верно. При компиляции у тебя может быть один SomeSuperClass класс, а при выполнении окажется совсем другой. Более того, он даже по методам/полям может быть местами не совместим. R>Совсем не понял, разжуйте.
Компилируем такие классы:
public class A {
public void doSome() {
System.err.println("A.doSome()");
}
public void doSome2() {
System.err.println("A.doSome2()");
}
}
// Вызываем один метод если параметров не передано, и другой -- если передано.public class B extends A {
public static void main(String[] args) {
B b = new B();
b.invoke(args.length == 0);
}
public void invoke(boolean flag) {
if(flag) {
doSome();
} else {
doSome2();
}
}
}
Потом в отдельном каталоге компилируем такой класс:
public class A {
public void doSome() {
System.err.println("OtherA.doSome()");
}
}
Копируем туда B.class с первого каталога и запускаем:
wfrag@fragentoo ~/1/2 $ java -cp . B
OtherA.doSome()
wfrag@fragentoo ~/1/2 $ java -cp . B some
Exception in thread "main" java.lang.NoSuchMethodError: B.doSome2()V
at B.invoke(B.java:11)
at B.main(B.java:4)
Как видишь, у класса B магически подменился предок безо всяких перекомпиляций, причём у этого предка даже метода одного не хватает.
WF>>Тут тоже не всё так просто. Очень часто композиция фиксируется однажды (например, IoC контейнером при запуске приложения) и не меняется во время работы. R>Фиксация времени выполнения — это выполнение процессором некоторого набора команд. Фиксация времени компилирования — это фиксация состава этого набора команд. Разницу совсем не видите, да?
О какой фиксации идёт речь? Что именно фиксируется-то?
В случае Java по большому счету отличие лишь в том, что в случае наследования вызов некоторого функционала делается на параметре this (неявный параметр при вызове методов), а в случае композиции -- на некотором поле (т.е сначала получаем значение поля, потом делаем вызов).
Здравствуйте, LaptevVV, Вы писали: LVV>Ну почему... У меня вот обратная картина: привык мыслить функционально... ОО-решение приходит только во вторую очередь, да и то, хорошенько подумать нужно...
Мне чем нравится наследование — это уменьшение дублирования кода. Естественно, это не значит, что надо наследоваться от чего попадя. Надо все хорошенько продумать. При этом статическая особенность наследования относительно композиции, подобно статической и динамической типизации, и для С++ является более логичным. (опустим флем про минусы С++, о которых и так много сказано) LVV>Вот писал интерпретатор виртуальной машины... Сходу написал все на функциях... Основной цикл процессора вызывает нужную функцию по указателю из массива указателей. А код операции является индексом. LVV>А потом только подумал, что можно было определить абстрактный класс Команда и наследоваться от него для реализации каждой команды. LVV>Пока реализовал промежуточное решение: разделил все на 2 дружественных класса: класс-процессор и класс-система команд. Инкапсулировал все функции-команды в класс система-команд как статические. Дальше буду реализовывать нормальный ОО-подход.
Писал я как-то эмулятор для ICFP-2006 UM машины... =) написал, замечательно... увидел реализацию с JIT. попытался обогнать хитрым исполнением — упирается в предсказание ветвлений проца, т.к. при выборке из массива все предсказания идут лесом. проигрыш — гигантский!
LVV>Это я опять к топику "что плохого в С++". Сильно много свободы. Одну и ту же работу — в трех разных видах делаю. Правда плюс есть — можно сравнивать подходы на практике. Но это только мне как преподу и пригодиться. А в реальном программировании сравнивать некогда — "копать" надо... LVV>Вот и получается, что в большом проекте каждый пишет как привык, а не так, как нужно...
Это да, но я не считаю, что от наследования надо отказываться... не надо вдаваться в крайности!
А то получается, что ради использования или НЕ использования какой-то вещи, могут пожертвовать даже тем, ради чего это все замышлялось...
P.S. А вот пример генерации кода для инструкций UM машины, создается около 3.5к функций для каждой инструкции.
Делалось под VS6 компилятор, но должно компилится и на остальных. Из-за отсутствия частичной специализации пришлось идти на стандартные ухищрения, а так было бы меньше кода. Компилятору надо указать, чтоб памяти кушал побольше.
VS7.0 компилится не хочет — падает по INTERNAL COMPILER ERROR. Но компилятся должно 100%.
[skipped] WF>Копируем туда B.class с первого каталога и запускаем:
[skipped]
В контексте обсуждения... вы это все серьезно?
WF>Как видишь, у класса B магически подменился предок безо всяких перекомпиляций, причём у этого предка даже метода одного не хватает.
Ню-ню.
WF>О какой фиксации идёт речь? Что именно фиксируется-то?
Вы сами ответили на свои вопросы, смотрите: WF>В случае Java по большому счету отличие лишь в том, что в случае наследования вызов некоторого функционала делается на параметре this (неявный параметр при вызове методов), а в случае композиции -- на некотором поле (т.е сначала получаем значение поля, потом делаем вызов).
Или тоже самое другими словами: Re[11]: паранойя наследования
Здравствуйте, rsn81, Вы писали:
R>[skipped] WF>>Копируем туда B.class с первого каталога и запускаем: R>[skipped] R>В контексте обсуждения... вы это все серьезно?
Легко. Компилировали с одной версией библиотеки, запустили с другой. В Java это сплошь и рядом.
Это просто пример, что никакой особой "фиксации" не происходит. Вся разница — в отсутствии лишней косвенности и всё.
WF>>Как видишь, у класса B магически подменился предок безо всяких перекомпиляций, причём у этого предка даже метода одного не хватает. R>Ню-ню.
Не совсем. Я говорю о том, что по большому счёту вся разница — в небольшой добавочной косвенности при вызове. На производительность это вряд ли особо влияет.
R>Кстати, не знаю, чего вы так прицепились к Java... это не только там так.
Просто я более-менее хорошо знаю, как там внутрях всё устроено. Так вот вызов "своего" метода от вызова "чужого" метода особо ничем не отличается.
Здравствуйте, igna, Вы писали:
I>Если статическая композиция достаточна, не стоит использовать наследование только для того, чтобы съэкономить на написании forwarding functions.
Тогда я не понял в чем бонус композиции перед наследованием, если: R>Композиция R>Плюсы R> определяется динамически за счет перекрестных ссылок (не связаны руки компилятором, во время исполнения любой объект может быть подменен другим соответствующего типа);
динамическую природу вырезали, т.к. она не обязательна... R>применяется на основе взаимного соблюдения классами контрактов друг друга, предпочтительно основанных на интерфейсах, то есть не нарушается принцип инкапсуляции;
При использовании наследования тоже не нарушается принцип инкапсуляции. Доступ к предку может быть ограничен и т.п. R>не помню, писали об этом GoF-ы или нет: такие структуры объектов проще понимать, чем сложные графы наследования, ведь композиция приводит к более простым графам наследования, принуждает постепенно писать ориентируясь на шаблоны проектирования, в первую же очередь на основной принцип — одной ответственности класса.
это очень предвзято... сказать, что композиция хоть как-то упрощает структуру кода.. не думаю...наследование может быть приватным... =)
Аналогично: не стоит использовать композицию ради того, чтоб избавится от наследования?
Здравствуйте, WFrag, Вы писали:
WF>Легко. Компилировали с одной версией библиотеки, запустили с другой. В Java это сплошь и рядом. WF>Это просто пример, что никакой особой "фиксации" не происходит. Вся разница — в отсутствии лишней косвенности и всё. WF>Что ню-ню? B-то не перекомпилировали.
Разговор в теме идет (по-крайней мере начинался) о проектировании в ООП, а не о хаках в конкретных ООЯ. Вы с продуктивом такую петрушку, что описали, устраиваете? Да, было дело, раньше и сам делал подобное даже с обфусцированными условно платными библиотеками. Но ведь нельзя сказать, что это имеет отношение к проектированию мною этих библиотек.
WF>Не совсем. Я говорю о том, что по большому счёту вся разница — в небольшой добавочной косвенности при вызове. На производительность это вряд ли особо влияет. WF>Просто я более-менее хорошо знаю, как там внутрях всё устроено. Так вот вызов "своего" метода от вызова "чужого" метода особо ничем не отличается.
Говорю про отличия this и this.someReference с точки зрения осведомленности. Вы зря все считаете, что я тут что-то выдумываю, на самом деле тупо цитирую из GoF — книга перед глазами.
Если я правильно понял, в своих проектах вы принципиально не испольуете наследования вообще?
LCR> Наследование классов (в т.ч. и множественное) полностью мажорируется комбой наследование_интерфейсов-реализация (а реализация соответственно — через композицию), так как не подвержена проблеме сильного связывания между классом и подклассом (и "diamond problem" в случае множественного наследования). А если эту комбу навернуть до трейтов-миксинов, то мажорируется в плане удобства программирования тоже.
Ага, в С тоже нет наследования, соответственно нет и "Diamond problem". Может писать на С?
Возможно, мне не понятно, чем усиляется связь между классом и подклассом при наследовании? При наследовании и добавлении собственных методов вы можете даже не использовать те типы, которые использует базовый класс. Композиция же вас обязует написать все те методы, которые реализует интерфейс класса, соответственно связывание между модулями и классами услиливается, даже если мы всего лишь добавляем пару методов.
В реальной жизни не для каждого класса надо создавать интерфейс. Классы вообще-то являются сущностью и необязательно будет другая их реальзация. Зачем преждевременно все усложнять??
LCR>Сегодня isa, а завтра уже isnota. Или там вставить доп. класс между родителем и потомком (пирожок уже подрумянился, но теперь нужно просверлить дырку и затолкать мясо). Реалии жызни-с...
А если завтра все в корзину, может сразу ничего не писать?
Реалии жизни — проблем с наследованиему меня никогда небыло, т.к. оно реализуется на уровне языка. Грамотное наследование — проблема проектирования. Но всегда можно забыть что-то дописать когда пишешь вручную, реалии жызни-с...
Т.к. наследование выделено на уровне языка в отдельную конструкцию — т.е. не смешано с остальными атрибутами класса, поэтому, я считаю, легче читается.
Композиции — это хорошо, и стратегии мне представляются одним из самых удобных и гибких способов композиции, жаль, что язык C# не очень удобен для применения этого паттерна, из-за отсутствия доступа к статическим членам генериков в дизайне и невозможности указать требование для генерика конструктора определённой сигнатуры. Всё решаемо, конечно, буквально одним-двумя лишними методами в контрактах, но это портит красивую картинку самого метода декомпозиции. Правда, и с этим борятся, через аттрибуты, рефлекшен, а вон в новом WPF — и через XML, повышая долю декларативного при использовании этого подхода.
Интересно, что сами стратегии на практике удобно реализовывать именно как некое дерево наследования, но при этом монстроидальное "общее" дерево наследования системы классов может распасться на малосвязанные небольшие деревца. В самом наследовании реализации никакого криминала нет, скорее — наоборот, просто текущие негативные высказывания относительно наследования реализации существуют из-за потери чувства меры при проектировании системы типов, где, в результате построения ветвистых деревьев наследования, мы получаем не упрощение, а усложнение структуры.