Поделитесь пожалуйста опытом, какими критериями выбора между наследованием и композицией вы пользуетесь для решения применить ли has-a или is-a отношения между классами? естественно хочется услышать о случаях, когда такое решение сделать не просто в силу "абстрактности" =) т.к. в совсем очевидных ситуациях (типа объект реального мира) выбор также очевиден.
иногда не всегда удается поэтому методом еволюции всегда можно прийти к оптимальному варианту ошибится сложно Код (Text): class children{} class room {} а вот Код (Text): class father {} class mother{} class child {} сложнее)) ребенок может быть не от того папы, но папа будет влиять на его воспитание и ребенок может получить восптание и привычки нового папы ну итд)) но гараздо более интерен вопрос как спроектировать так что бы не получился быдло код
NeuronViking ну вообще это какой-то глобальный вопрос... а думаю, нужно более конкретно задать его... приведите пример нескольких классов, и можно будет обсудить то, как построить взаимоотношения между ними... мне на практике гораздо чаще приходилось делать интерфейсный класс и несколько наследников от него, разнящихся реализацей интерфейсных методов... с множественным наследованием как-то мало приходилось работать... вот кстати идеологически пример, в принципе позволяющий генерировать наследников разных родителей и наследников от наследников))): Код (Text): class mother {...}; class mother1 {...} : public mother; class mother2 {...} : public mother; ... class father {...}; class father1 {...} : public father; class father2 {...} : public father; ... class child {...} : public mother, public father;
_DEN_ я это знаю. именно поэтому и спрашиваю не о правилах, а прошу поделиться конкретным опытом в том или ином случае. если бы были правила, то жизнь была бы простой и веселой =)
NeuronViking Чтобы ответить на твой вопрос, нужно не другим делиться своим опытом, а тебе рассказать о своей задаче.
Множественное наследование всегда ругали. ИМХО, правильно и по делу. Тупо наследовать, тоже не вариант. Наследование подразумевает (кроме совсем уж тривиальных случаев!) использование виртуальных методов. А с виртуальными методами, не все так просто, хотя и естественно . Вот, где-то от сюда , и далее маленький тред на тему C++ vs. Java (типа, ява шустрее плюсов . От себя добавлю, что подобное использование виртуальных методов тоже относится к "быдлокоду", ИМХО: Код (Text): class Toggle { .... virtual Toggle& activate(); .... }; class NthToggle : public Toggle { Toggle& activate(); }; Toggle *toggle = new Toggle(val); .... NthToggle *ntoggle = new NthToggle(val, 3); Смысл их тогда делать виртуальными, эти методы? Все равно обращаешься не через указатель на предка, не проще ли тогда завести базового предка для обоих классов ( для общего кода обоих классов ), а эти классы независимо породить из него? Лично мне больше по душе подход Александреску, описанный в его буке "Современное проектирование на С++", где он описал вариант реализации паттерна "Стратегия", с использованием шаблонов. Не смотря, на то, что это реализуется через наследование от нескольких базовых классов, ИМХО, больше похож на композицию, когда терминальный класс порождается из нескольких тривиальных базовых, и не образуется сложных и разветвленных иерархий классов. Подобный подход, реализован и в STL. Не смотря, на то что там, например, итератор наследуется от базового класса, не уверен, что это можно назвать "классическим" наследованием. Собственно, сам сейчас ориентируюсь на подобное написание кода. Здесь-то, как раз, вопрос не в задаче, а в концепциях написания хорошего кода -- как писать, так "чтоб потом не было мучительно больно за бесцельно потраченное время" (С). А вообще, ИМХО, в рамках поставленного вопроса, абсолютно равноценно применить ли has-a или is-a отношения между классами. Если посмотреть на низком уровне то, фактически при композиции, полученный класс является фасадом для вложенного и его методы по большему счету пересылают значения вложенному классу ( может быть, с небольшой обработкой ), и при оптимизации будут проинлайнены ( при условии не виртуальности ). В случае же наследования, просто будет вызываться метод предка. Т.ч. оверхеда по коду можно избежать. А вообще, вот цитата с intuit.ru: В общем, реляционная алгебра и теория реляционных баз данных, оказывается существенно пересекается с ООП.
Damon Если нет четкого понимания, то никакие чужие концепции тебе не помогут - пройдет пол года-год, и архитектура развалится по частям.
_DEN_ Вообще-то, под концепциями я и понимаю "четкое понимание" (С), "разложенное по полочкам" (С)! Мы говорим об одном, только чуть по разному. А вообще, "четкое понимание" (или концепции построения иерархии классов), в целом не зависит от конкретной задачи. Конкретная задача, лишь, вносит небольшие вариации, не более.
Damon Понимание того, как проектировать архитектуру в принципе, какими соображениями руководствоваться при выстраивании взаимоотношений между сущностями, и т.д. - это всего лишь, так скажем, средняя общеобразовательная школа. Для того, чтобы научиться проектировать качественные решения в конкретной области (игры, серверы, лексические анализаторы, веб-сервисы, компиляторы, и т.д.), нужно потратить довольно много времени, решая задачи в этой конкретной области.
_DEN_ М-м-м... А Вам не кажется, что Вы чуть смешиваете понятия? Давайте для примера возьмем компилятор (почему бы и нет?). Как работает компилятор ( в первом приближении, подробнее -- книжка Ахо-Ульмана ): 1) парсится исходник и разбивается на лексемы ( лексический анализ ); 2) поток лексем подвергается синтаксическому анализу и строится дерево разбора; 3) к дереву применяются различные оптимизации; 4) по дереву генериться код. Теперь вопрос -- каким боком перечесленные пункты ( реализация которых в сумме дает качественный компилятор ), относятся к ООП и композиции или наследованию классов? Я знаю компиляторы, вообще, без привлечения ООП написанные! GCC -- на C, к примеру. Я к тому, что "архитектура, в принципе", может быть реализована и с помощью императивного программирования. Как пример крупных проектов -- ядро пингвина (врядли кто-то скажет, что мелкий проект). Т.ч., пожалуйста, не путайте алгоритм из конкретной предметной области и отношения классов. Это, как стратегия и тактика. Стратеги, лишь, отдают приказ, реализация же, целиком и полностью отдается на откуп тактикам!
Damon Это все пустое. Напишите хотя бы один компилятор от начала и до конца самостоятельно, тогда и поймете о чем я говорю. А я знаю программно-аппаратный комплекс, работающий с аптаймом в 34 года в полной изоляции, и написанный на ассемблере. Алгоритмы тут не при чем. Я говорю про архитектуру.
_DEN_ Да я и так понимаю, о чем Вы говорите, мнение я высказал выше. Пока, никакой конкретики я от Вас не услышал, только общие фразы... Повторюсь, приведите конкретный пример, типа: "... Все хотели ( и по теории это было правильно! ) использовать наследование и тут прихожу я и говорю: -- Пацаны ( мужики, etc. [ не нужное зачеркнуть! ] )! А давайте будем использовать композицию классов! И использовали композицию и стало всем хорошо!" Или, наоборот наследование. Или, Вас не послушали и стало всем плохо... Или не Вас, а какого-либо гуру, не принципиально! В общем, давайте побольше конкретики! Правда же интересно, где я заблуждаюсь.
И почему я упорно не хочу в это верить? Что под этим подразумевается? Гoвнoкoд на С++ работает в 20 раз медленнее, чем в жабе? Давайте свой тест проведём, потому что у меня уж оооооочень сильные сомнения - пока сам ни увижу - не поверю.
Да всё это фигня по-моему концепции эти, как средняя температура по больнице. Это катит чтобы книжки писать или бабло выбивать под супер-пупер концепции. Если ты врубился в предметную область, то понимаешь какие объекты как соотносятся. Если нет, то не помогут концепции - всё равно ошибёшься.
cupuyc Там чуть ниже по треду я высказал свои соображения. Фактически, если ставить компиляторы в одинаковое положение, то видно, что ява проигрывает плюсам при вызове виртуальных методов в 2 раза -- см. на графике "клиентскую яву". Далее по коду возникает вопрос, если тестировать вот такой код, сумеет ли ява оптимизировать его: Код (Text): Toggle *toggle = new Toggle(val); .... toggle = new NthToggle(val, 3); (это плюсовый код, для явы искать не охота, но там идентично будет!) ИМХО, это более корректный вариант вариант тестирования виртуальных методов!
согласен только звучит это не так), звучит это так что бы решать конкретную задачу в какой то области, нужно досконально разбиратся(быть специалистом) в этой области то что ява быстрее С++ придумали жависты))