Re[5]: Объясняем наследование
От: 0BD11A0D  
Дата: 31.10.14 14:20
Оценка:
Здравствуйте, Tilir, Вы писали:

BDA>> А ежели кто просит примеров для объяснения другим, я всегда сильно подозреваю недостаточное собственное понимание.


T>ЧСВ over 9000?


Нет, жизненный опыт. Один дурачок мне глаза открыл дюжину лет назад. Он мне доказывал что-то типа распространения бесплатного образования в Германии, весьма оригинальным способом: зашел на свой любимый трепный форум и попросил накидать примеров. Ему накидали ссылок на немецком, он их притащил мне — на, типа, переводи и читай сам. Доказано Занусси. Я офонарел от этого и впервые задумался, что у других мышление может отличаться. Для меня просить примеры для доказательства или объяснения — дико. Это показатель (не)владения темой, то есть, повод задуматься, откуда я взял те мысли, которые собираюсь доказывать или объяснять. С тех пор я этот паттерн то и дело встречаю в жизни. Но пока не доводилось у преподавателей.

T>Ни у кого, никогда не может быть достаточного понимания языка C++. Даже его создатель знает его на 6/10. Впрочем, учитывая дальнейшее...


Тема называется «Объясняем наследование». C++ я знаю балла на 3 из 10 и не стесняюсь в этом признаться. За точку отсчета в 10 баллов я беру Пашу Кузнецова, который стандарт знает назубок и одного чувака, который меряет for fun производительность метапрограммирования разных компиляторов. А вы не читаете, что вам пишут. Красной, как говорится, нитью, проходило у меня: не путайте наследование в C++ и наследование вообще. C++ полон чудес и legacy-ограничений.

Теперь я проверил то, что и подозревал: учить наследованию просто вы не собирались. Воспитываете еще одно поколение упертых и зашоренных плюсовиков.

T>Я, пожалуй, соберу ваши основные мысли в кучку, слитное чтение всего этого бугурта немного поднимает мне настроение:


Не все то бугурт, что воняет. Бывает просто банан критика.

BDA>> В одном древнем языке понятие интерфейса с его имманентным ограничением на отсутствие какой бы то ни было реализации отсутствует.

BDA>> Язык слишком... либеральный. То в нем в интерфейсах реализация допускается, то неоткрытое наследование. Кто-то потом на подобной уродливости строит грязные хаки («потому, что могут», ага), а их дурацкий комитет потом не дает запретить. Так это и надо рассказывать.
BDA>> Недометаданные в языке без базового класса Object выродились в уродливый костыль. Наследование тут не при чем. Нужда в нем обычно возникает, когда в реальной программе под Уиндоус приходится гонять через WinAPI объекты.
BDA>> Это не академическое ковыряние в носу, извините. Это жизнь и зарабатывание денег. Чему я бы и учил в первую очередь.

T>В общем, с вами всё ясно. Вступать в диспуты с дотнетчиками здесь я считаю таким же оффтопом как и с джавистами. Интернет также полон ими. Ваш тон offensive enough, но и я со своей стороны презираю скриптоязыки для виртуальных машин и в общем согласен на взаимность. Посоветовать что-либо вы мне вряд ли сможете.


Я не дотнетчик. А шарп с явой не скриптоязыки, JFYI. Но вы мне открылись с новой стороны. Вот с этой: http://rsdn.ru/forum/job/5746297.1
Автор: 0BD11A0D
Дата: 18.08.14


T>Но не удержусь прокомментировать главный перл:


BDA>>Что касается деструктора, это обработчик события уничтожения, что на нем останавливаться? Что в нем такого особого?


T>Понимаете, виртуальный деструктор это то, что надо знать в первую очередь и как отче наш. Зачем он нужен, что будет если его не объявить и отнаследоваться и т.д.


Нет, не понимаю. Сделайте то же самое с обработчиком любого другого события — получите глюк. И этот (утечка ресурсов) даже не самый страшный. Самый страшный — порча памяти, затрагивающая vtbl. Вот где жопа, но на нее хитрый болт пока не придумали. GC, разве что.

>Но не в вашем случае, когда вы скриптуете виртуальную машинку и за вами всё чистит сборщик мусора, а в случае, если мы пишем на языке, где память это ресурс и им надо управлять.


Дотнетовский рантайм не виртуальная машинка. Офенсив офенсиву рознь. Потому, что вы уже начали искажать факты.

UPDATE: а впрочем, черт его знает. Все это майкрософтовский маркетинг, по большому счету. Продемонстрировать преимущество перед Джавой прямо с порога. Забираю назад предыдущий абзац.
Отредактировано 31.10.2014 14:28 0BD11A0D . Предыдущая версия . Еще …
Отредактировано 31.10.2014 14:23 0BD11A0D . Предыдущая версия .
Re[9]: Объясняем наследование
От: vpchelko  
Дата: 01.11.14 00:17
Оценка:
Здравствуйте, Tilir, Вы писали:

T>Не к наследованию, а к вашему примеру наследования на примере List, Set, etc. Необходимость тянуть List от ICollection вызвана именно отсутствием нормальных шаблонов, о чём вам сразу и было сказано.


Я этого не заметил.
А что плохого, что они наследуются от Collection и Iterable?
Сало Украине, Героям Сала
Re: Объясняем наследование
От: Dufrenite Дания  
Дата: 03.11.14 08:55
Оценка:
Здравствуйте, Tilir, Вы писали:

Самое простое, это объяснить на примере компьютерной игры.

Привожу пример из жизни:

1.
class Actor;

2.
class Character : public Actor;
class Vehicle : public Actor;
class Trigger : public Actor;
class Door : public Actor;

3.
class Player : public Character;
class Enemy : public Character;

class Tank : public Vehicle;
class Helicopter : public Vehicle;

...

Можно добавить ещё иерархию:

1.
class CharacterController;

2.
class PlayerController : public CharacterController;
class EnemyController : public CharacterController;

3.
class LocalPlayerController : public PlayerController;
class RemotePlayerController : public PlayerController;

...
Re[2]: Объясняем наследование
От: 1303  
Дата: 03.11.14 21:13
Оценка:
Здравствуйте, Dufrenite, Вы писали:
...
D>Самое простое, это объяснить на примере компьютерной игры.

D>Привожу пример из жизни:


D>1.

D>class Actor;

D>2.

D>class Character : public Actor;
D>class Vehicle : public Actor;
D>class Trigger : public Actor;
D>class Door : public Actor;

D>3.

D>class Player : public Character;
D>class Enemy : public Character;

D>class Tank : public Vehicle;

D>class Helicopter : public Vehicle;

D>...


Да, действительно просто. Только поясни — как с помощью этой иерархии найти все юниты, которые летают?
Re[3]: Объясняем наследование
От: Dufrenite Дания  
Дата: 04.11.14 05:37
Оценка:
Здравствуйте, 1303, Вы писали:

1>Да, действительно просто. Только поясни — как с помощью этой иерархии найти все юниты, которые летают?


1. IVisitor
2. virtual bool IsFlying()
...
1000. dynamic_cast<IFlyingUnit>()

Дайте контекст — обсудим.
Re[4]: Объясняем наследование
От: 1303  
Дата: 04.11.14 21:00
Оценка:
Здравствуйте, Dufrenite, Вы писали:
...
1>>Да, действительно просто. Только поясни — как с помощью этой иерархии найти все юниты, которые летают?

D>1. IVisitor

D>2. virtual bool IsFlying()
D>...
D>1000. dynamic_cast<IFlyingUnit>()

Так ведь иерархия не используется. С таким же успехом можно обойтись вообще без наследования.

D>Дайте контекст — обсудим.

А я вот именно не это и намекаю — без контекста и понимания логики построение иерархии — мартышкин труд. В лучшем случае он пропадёт, а в худшем — приведёт к проблемам в будущем.
Re[5]: Объясняем наследование
От: Dufrenite Дания  
Дата: 04.11.14 22:28
Оценка:
Здравствуйте, 1303, Вы писали:

1>Так ведь иерархия не используется. С таким же успехом можно обойтись вообще без наследования.


Я понял в чём дело. У меня так называемый data-oriented подход. Главное правильно разместить данные, а алгоритмы обхода делаются по мере надобности. Все другие способы построения иерархий классов, в частности с привязкой к реальному миру приводят к эпик-фейлам.

1>А я вот именно не это и намекаю — без контекста и понимания логики построение иерархии — мартышкин труд. В лучшем случае он пропадёт, а в худшем — приведёт к проблемам в будущем.


Я вам привёл пример как это делается в реальный проектах. В частности, эта иерархия мной взята (по памяти, возможно не совсем точно) из Unreal Engine 4.
Re[6]: Объясняем наследование
От: 1303  
Дата: 04.11.14 23:02
Оценка:
Здравствуйте, Dufrenite, Вы писали:
...
1>>Так ведь иерархия не используется. С таким же успехом можно обойтись вообще без наследования.

D>Я понял в чём дело. У меня так называемый data-oriented подход. Главное правильно разместить данные, а алгоритмы обхода делаются по мере надобности.

Не понял — что значит правильно разместить данные.

1>>А я вот именно не это и намекаю — без контекста и понимания логики построение иерархии — мартышкин труд. В лучшем случае он пропадёт, а в худшем — приведёт к проблемам в будущем.


D>Я вам привёл пример как это делается в реальный проектах. В частности, эта иерархия мной взята (по памяти, возможно не совсем точно) из Unreal Engine 4.


Ну вот а в другом реальном проекте будет совершенно иное. Но предлагаю тут с этим закругляться, тема про проблемы ООП где-то рядом была.
Re[4]: Объясняем наследование
От: Sinclair Россия https://github.com/evilguest/
Дата: 06.11.14 07:33
Оценка:
Здравствуйте, Куть, Вы писали:
К>Эту иерархию классов можно использовать в написании простенького графического редактора.
Начинается...
Нельзя!
К>На примере фигур можно показать, что код с наследованием получается проще и понятнее:
К>1. Можно нарисовать все фигуры, пробежав по списку указателей std::list<Figure *> и вызывая виртуальный метод draw().
К>2. Можно найти сумму площадей в пару строчек.
К>3. Можно сохранить данные всех фигур в файл, заодно можно рассмотреть проблему с чтением из файла и её решение в виде фабричного метода.

К>А перед этим можно показать пример, где часть этих задач решается без наследования, с помощью длинных switch(type) или указателей на функции.

А после этого можно показать пример, где все эти задачи решаются без наслежования и без switch, и без указателей на функции.
Вменяемый инженер всё сделает через примитив Path, состоящий из прямых, арок, и кривых Безье, а все остальные "фигуры" будут просто конструкторами Path с различными аргументами.
Потому, что все ваши "простые и понятные" примеры сдохнут сразу же, как я попрошу вас реализовать мне операцию Intersect для двух фигур.

К>И добить добавлением нового типа фигуры. С наследованием будет добавлен один класс (изменения локализированы), а без наследования изменения будут размазаны по всей программе.

А без наследования все изменения сведутся к написанию ещё одной функции.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re: Объясняем наследование
От: Sinclair Россия https://github.com/evilguest/
Дата: 06.11.14 07:59
Оценка: +2
Здравствуйте, Tilir, Вы писали:

T>Но весь этот план требует некоего сквозного примера для которого проводить объяснения. И вот тут у меня затык.


T>Все книги по C++ которые объясняют наследование вызывают желание убить себе лицо рукой.

Молодец! Никого не слушай — всё правильно думаешь.

Я бы начал вот с чего:
1. Объяснил бы концепцию полиморфизма вообще. На примере, сначала, одной функции. Показываем, как можно добиться интересных эффектов путём параметризации алгоритма функцией. Объясняем, что это в жизни ещё очень пригодится, и что одного этого приёма хватает в очень многих случаях.
Пример — поиск в массиве.
Развитие примера — поиск в дереве. Пока что даже нет нужды вводить какие-либо классы; дерево вполне может быть plain struct. Главная идея — объяснить, зачем вообще разделять код итерирования по дереву и код предиката: на переборе массива неочевидно, что find(array, less_or_equal_to_5()) лучше, чем for(int i=0;i<arraayLen; i++) if array[i]<=5 { res = i; break;}.
Здесь как бы уже возникает дихотомия между интерфейсом (сигнатурой функции) и реализацией — реальной функцией.

2. Объяснил бы тот момент, когда придётся отказаться от функции, и перейти к объектам. Например, хранение состояния — например, агрегирующая функция. Типа как нам подсчитать сумму/среднее/минимум/максимум элементов в массиве/дереве/и т.п?
Теперь у нас есть интерфейс IAggregator (c одним методом), и возможность реализовать разные алгоритмы агрегирования.

3. Расширил бы пример с введением интерфейса из нескольких функций, которые должны работать согласованно. Например, с умножением матриц — если мы берём пару из (*, +), то получаем классическое перемножение матриц. А если берём пару из (+, min), то мы находим кратчашие пути при умножении матрицы смежности самой на себя.

4. В качестве домашнего задания предложил бы реализовать класс "калькулятора" — т.е. штуки, которая умеет вести вычисления с плавающей запятой. При этом надо сделать две реализации — одну "быструю", с табличной реализацией тригонометрии и загрублённой арифметикой с бит-шифтами, другую "точную", через сопроцессор. И сравнить результаты решения одной и той же численной задачи с использованием двух таких калькуляторов.

5. Напоследок бы сказал, что ситуация, когда используется более одного уровня наследования — экзотика, которая скорее всего им в жизни потребуется очень-очень нескоро. И что когда кажется, что есть повод такую иерархию ввести (например, отнаследовавшись от класса, реализующего интерфейс), надо сначала посмотреть, нет ли способа решить задачу без наследования.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[7]: Объясняем наследование
От: Dufrenite Дания  
Дата: 07.11.14 09:11
Оценка:
Здравствуйте, 1303, Вы писали:

1>Не понял — что значит правильно разместить данные.


Термин "декомпозиция" вам знаком?
Re[5]: Объясняем наследование
От: Куть  
Дата: 07.11.14 16:00
Оценка:
Вы всё правильно говорите, и про intersect() я сам думал — писать или нет. Видимо надо было написать.
Ключевое слово — "простенький" редактор. Такой, в котором не нужно искать пересечения и использовать математику сложнее школьной для расчета площадей фигур.
Плюс графического редактора — наследование можно объяснить в терминах, понятных последним двоечникам. Тогда как понятия "кривая Безье" или "агрегатор" могут вообще ничего не говорить половине студентов.

Ваше предложение ниже мне однозначно нравится больше, но вариант с редактором тоже имеет право на существование.
Re: Объясняем наследование
От: diez_p  
Дата: 07.11.14 17:46
Оценка: -1
Здравствуйте, Tilir, Вы писали:
T>У меня тут есть немного студентов на нашей базовой кафедре, которых надо научить плюсам в свободное от работы время. Пока всё шло хорошо, но скоро надо будет объяснить им наследование. Пока план примерно такой:

Наследование идет от АТД (Абстрактный Тип Данных), под наследование можно загнать все что угодно.
Например мебель. Бизнес область домашний интерьер под ключ. Визуализация транспортировка и сборка.

Есть базовый интерфейс мебель. Например компания по обстановке мебели в квартире.
ЭлементМебели{атрибуты: размеры, вес, хрупкий, занимаемый объем в пространстве} абстратный
стол|стул|шкаф|лежак — наследники ЭлементМебели тоже абстрактные типы, добавляют свои атрибуты — специфицируют базовый интерфейс.

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

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

Это первое что пришло в голову.
Re[6]: Объясняем наследование
От: Sinclair Россия https://github.com/evilguest/
Дата: 07.11.14 19:44
Оценка:
Здравствуйте, Куть, Вы писали:

К>Вы всё правильно говорите, и про intersect() я сам думал — писать или нет. Видимо надо было написать.

К>Ключевое слово — "простенький" редактор. Такой, в котором не нужно искать пересечения и использовать математику сложнее школьной для расчета площадей фигур.
К>Плюс графического редактора — наследование можно объяснить в терминах, понятных последним двоечникам. Тогда как понятия "кривая Безье" или "агрегатор" могут вообще ничего не говорить половине студентов.
Опасность этого эксперимента — в том, что многие и до седин упорно считают, что именно так и надо писать графические редакторы.
Получается какой-то парадокс: мы берём концепцию, которая должна помогать нам решать сложные задачи, и внезапно оказывается, что всё наоборот — для синтетической упрощённой задачи она ещё хоть как-то подходит, а стоит задачу чуть усложнить — всё, приплызд, отматывай назад. Вся диаграмма классов летит в корзину, объём кода сокращается в шесть раз, функциональность вырастает в два.
Я, будучи на месте вот такого "студента", почувствовал себя, мягко говоря, обманутым в момент осознания.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[8]: Объясняем наследование
От: 1303  
Дата: 09.11.14 22:38
Оценка:
Здравствуйте, Dufrenite, Вы писали:
...
1>>Не понял — что значит правильно разместить данные.

D>Термин "декомпозиция" вам знаком?

В общих чертах. Правда, я не уверен, что это как-то связано с наследованием.
Re[6]: Объясняем наследование
От: Sinclair Россия https://github.com/evilguest/
Дата: 10.11.14 11:34
Оценка: +1
Здравствуйте, Dufrenite, Вы писали:

D>Я понял в чём дело. У меня так называемый data-oriented подход. Главное правильно разместить данные, а алгоритмы обхода делаются по мере надобности. Все другие способы построения иерархий классов, в частности с привязкой к реальному миру приводят к эпик-фейлам.

Вот тут опять есть нюансы. На моей памяти, попытка выстроить иерархию наследования на основе данных как раз и приводит к эпик фейлам.
Классический пример — квадрат/прямоугольник/эллипс/круг. Вроде бы и про данные всё понятно, а иерархия никак не вытанцовывается.
Единственный понятный мне способ ОО-проектирования — это плясать от поведения.
Ну, то есть решаем задачу в стиле "ок, вот у нас есть идеальный "решатель задачи" для каждой сформулированной нами задачи. Давайте посмотрим, можно ли выстроить какую-то классификацию этих решателей; можно ли выделить из их ответственностей какие-то фрагменты, которые сделают общее решение проще и понятнее".

Важный момент — речь идёт про "решатель задачи", а не просто про "класс для представления сущности из предметной области".
Например, если у нас речь шла про геометрические фигуры, то "задача" — это типа "нарисовать на экране прямоугольник", и решатель, соотвественно — это "рисователь прямоугольников на экране", а вовсе не "класс Прямоугольник с методом Draw".
В результате такой декомпозиции мы быстро получим "рисователя отрезков на экране" и "рисователя ломаных на экране", а рисователи прямоугольников, треугольников, квадратов и прочих фигур исчезнут за ненадобностью.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[7]: Объясняем наследование
От: Dufrenite Дания  
Дата: 10.11.14 13:51
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>Здравствуйте, Dufrenite, Вы писали:


D>>Я понял в чём дело. У меня так называемый data-oriented подход. Главное правильно разместить данные, а алгоритмы обхода делаются по мере надобности. Все другие способы построения иерархий классов, в частности с привязкой к реальному миру приводят к эпик-фейлам.


S>Вот тут опять есть нюансы. На моей памяти, попытка выстроить иерархию наследования на основе данных как раз и приводит к эпик фейлам.

S>Классический пример — квадрат/прямоугольник/эллипс/круг. Вроде бы и про данные всё понятно, а иерархия никак не вытанцовывается.

Согласен, если рассматривать иерархию как "вещь в себе". А если рассмотреть её в контексте архитектурного паттерна, например MVC, то это имеет смысл.

В этом случае я бы спроектировал следующие взаимодействующие иерархии:

1. Model:
class Shape;
class Square : public Shape;
class Rectangle : public Shape;
...

1.1 Visitor:
struct IShapeVisitor;

2. View:
class ShapeDrawer : private IShapeVisitor;
class ShapeNetworkSender : private IShapeVisitor;
...

3. Controller:
class CanvasController;
...

S>Единственный понятный мне способ ОО-проектирования — это плясать от поведения.

S>Ну, то есть решаем задачу в стиле "ок, вот у нас есть идеальный "решатель задачи" для каждой сформулированной нами задачи. Давайте посмотрим, можно ли выстроить какую-то классификацию этих решателей; можно ли выделить из их ответственностей какие-то фрагменты, которые сделают общее решение проще и понятнее".

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

Если же отделить "мух от котлет", то есть данные от поведения, то можно получить минималистичные и чётко сфокусированные иерархии, не имеющие абсолютно ничего лишнего.

S>Важный момент — речь идёт про "решатель задачи", а не просто про "класс для представления сущности из предметной области".

S>Например, если у нас речь шла про геометрические фигуры, то "задача" — это типа "нарисовать на экране прямоугольник", и решатель, соотвественно — это "рисователь прямоугольников на экране", а вовсе не "класс Прямоугольник с методом Draw".
S>В результате такой декомпозиции мы быстро получим "рисователя отрезков на экране" и "рисователя ломаных на экране", а рисователи прямоугольников, треугольников, квадратов и прочих фигур исчезнут за ненадобностью.

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

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

2. View:
class ShapeDrawer : private IShapeVisitor;

4. Render:
class LineRenderer
{
public:
void DrawLines(const std::vector<Point>& points, const Color& color);
};
Re[7]: Объясняем наследование
От: diez_p  
Дата: 10.11.14 16:14
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>Здравствуйте, Dufrenite, Вы писали:


S>Единственный понятный мне способ ОО-проектирования — это плясать от поведения.

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

Классическая иерархия Figure.Draw нарушает SRP. Когда мы делали рисовалку на DirectX, то базовый объект у нас выдавал массив объектов которые надо отрисовать, рисовальщик был отдельно, т.к. алгоритм отрисовки и её необходимость не зависит от того когда объект рисуется.
Re[8]: Объясняем наследование
От: Sinclair Россия https://github.com/evilguest/
Дата: 10.11.14 18:27
Оценка:
Здравствуйте, diez_p, Вы писали:
S>>Единственный понятный мне способ ОО-проектирования — это плясать от поведения.
_>И в результате данные и поведение перемешиваются в винегрет, т.к. как типы данных сильно реже меняются, нежели чем алгоритмы их обработки,
Не вижу, как вы сделали переход от посылки к результату.

_>Классическая иерархия Figure.Draw нарушает SRP. Когда мы делали рисовалку на DirectX, то базовый объект у нас выдавал массив объектов которые надо отрисовать, рисовальщик был отдельно, т.к. алгоритм отрисовки и её необходимость не зависит от того когда объект рисуется.

Совершенно верно. Вы пришли к правильному решению задачи. Внезапно оказалось, что вместо развесистой иерархии фигур вы получили фиксированный набор примитивов.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[8]: Объясняем наследование
От: Sinclair Россия https://github.com/evilguest/
Дата: 10.11.14 18:34
Оценка:
Здравствуйте, Dufrenite, Вы писали:
D>Согласен, если рассматривать иерархию как "вещь в себе". А если рассмотреть её в контексте архитектурного паттерна, например MVC, то это имеет смысл.
Вот это меня опять настораживает. Я имею в виду — ввод паттернов на ровном месте.

D>В этом случае я бы спроектировал следующие взаимодействующие иерархии:


D>1. Model:

D>class Shape;
D>class Square : public Shape;
D>class Rectangle : public Shape;
D>...

D>1.1 Visitor:

D>struct IShapeVisitor;

D>2. View:

D>class ShapeDrawer : private IShapeVisitor;
D>class ShapeNetworkSender : private IShapeVisitor;
D>...

D>3. Controller:

D>class CanvasController;
D>...
Ух ты ж блин! Сколько всего. И всё это вместо пары классов Path и Canvas.
Круто, чё.

D>К сожалению, я сталкивался с ситуациями, когда попытки построить иерархию на основе поведения приводили к разрастанию иерархии до безумных размеров с совершенно головоломной логикой наследования.

Вынужден поверить вам на слово, хотя сам такого не видел.

D>Если же отделить "мух от котлет", то есть данные от поведения, то можно получить минималистичные и чётко сфокусированные иерархии, не имеющие абсолютно ничего лишнего.

Тут — согласен. Как раз чрезмерное увлечение слиянием данных с поведением и является обычной причиной чудовищных ОО-решений.

D>В этом случае дальнейшее развитие нашего модельного примера могло бы выглядеть как-то так:


D>2. View:

D>class ShapeDrawer : private IShapeVisitor;

D>4. Render:

D>class LineRenderer
D>{
D>public:
D> void DrawLines(const std::vector<Point>& points, const Color& color);
D>};
А какой код и сколько нам потребуется в ShapeDrawer? Я же правильно понял, что в нём будет по методу на каждого потомка Shape?
Простите, но такой код хорош только для списания затрат по time spent basis.
Для реальной работы надо минимизировать усилия. В вашем коде стоимость добавления новой фигуры — запредельна.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.