Всем привет! Есть многим известная иерархия классов: class Shape, от него наследуются Rectangle, Circle и тд..
Как можно нарисовать фигуру не храня в ней указатель на метод draw? Хотелось бы какую-то функцию DrawShapes(список Shape),
которая рисует список фигур, но как эта функция будет знать какую фигуру как рисовать? Вводить в фигуры id и делать кучу if?
Или есть способ получше?
Здравствуйте, whyisthat, Вы писали:
W>Всем привет! Есть многим известная иерархия классов: class Shape, от него наследуются Rectangle, Circle и тд.. W>Как можно нарисовать фигуру не храня в ней указатель на метод draw? Хотелось бы какую-то функцию DrawShapes(список Shape), W>которая рисует список фигур, но как эта функция будет знать какую фигуру как рисовать? Вводить в фигуры id и делать кучу if? W>Или есть способ получше?
Здравствуйте, whyisthat, Вы писали:
W>Всем привет! Есть многим известная иерархия классов: class Shape, от него наследуются Rectangle, Circle и тд.. W>Как можно нарисовать фигуру не храня в ней указатель на метод draw? Хотелось бы какую-то функцию DrawShapes(список Shape), W>которая рисует список фигур, но как эта функция будет знать какую фигуру как рисовать?
Стандартным в ООП считается использование паттерна Визитор в данном случае. Сначала может показаться, что особого смысла нет менять шило на мыло — функцию Draw() на Visit()/Accept(). Т.е. иерархию всё равно нужно затрагивать (если она изначально не была visitor friendly). Но профит в том, что Visit()/Accept() ты пишешь единожды, а дальше уже можешь добавлять методы Draw(), Print(), ComputeArea(), etc, не меняя исходной иерархии.
Идея паттерна Visitor заключается в переворачивании иерархии классов на бок. Для m классов и n методов ты будешь иметь иерархию из n визиторов по m функций посещения в каждом. Добавление нового метода над иерархией состоит в добавлении ещё одного визитора. Недостаток — при добавлении класса в иерархию, нужно добавить по методу в каждый из существующих визиторов (т.н. expression problem).
W>Вводить в фигуры id и делать кучу if? W>Или есть способ получше?
Id обычно уже есть, виде RTTI или object.GetType(). Ну или можно свои идентификаторы ввести. If'ы делать не надо, можно завести ассоциативный массив (например, хэш-таблицу) с ключом в этом id, и значением в виде функции.
Здравствуйте, whyisthat, Вы писали:
W>Всем привет! Есть многим известная иерархия классов: class Shape, от него наследуются Rectangle, Circle и тд.. W>Как можно нарисовать фигуру не храня в ней указатель на метод draw? Хотелось бы какую-то функцию DrawShapes(список Shape), W>которая рисует список фигур, но как эта функция будет знать какую фигуру как рисовать? Вводить в фигуры id и делать кучу if? W>Или есть способ получше?
Про визитор уже сказали, а ещё можно без ООП:
Фигуры — это алгебраические типы данных, метод Draw — это паттернматчинг.
В хорошем ЯП есть все необходимые проверки на этапе компиляции (например, что ни одну фигуру не зыбыли). Наличием этих проверок паттернматчинг лучше if-ов.
Минус — чтобы добавить новую фигуру, надо менять уже написанный ранее код, то есть нарушается Open Closed Principle. Но это далеко не всегда бывает важно.
Здравствуйте, whyisthat, Вы писали:
W>Или есть способ получше?
Про визитор уже сказали, альтернатива — виртуальный метод GetPath(), который описывает контур фигуры. Как нарисовать контур — проблема конкретного рендерера.
Здравствуйте, whyisthat, Вы писали:
W>Всем привет! Есть многим известная иерархия классов: class Shape, от него наследуются Rectangle, Circle и тд.. W>Как можно нарисовать фигуру не храня в ней указатель на метод draw? Хотелось бы какую-то функцию DrawShapes(список Shape), W>которая рисует список фигур, но как эта функция будет знать какую фигуру как рисовать? Вводить в фигуры id и делать кучу if? W>Или есть способ получше?
а что за язык?
в сишарпе для этого есть extension methods.
да и разные другие ситуации, где раньше была возможность применить
какой-нибудь клёвый паттерн навроде визитора, теперь покрыты языковыми
конструкциями просто напросто.
Меня постоянно преследуют умные мысли. Но я быстрее.
Здравствуйте, whyisthat, Вы писали:
W>Всем привет! Есть многим известная иерархия классов: class Shape, от него наследуются Rectangle, Circle и тд.. W>Как можно нарисовать фигуру не храня в ней указатель на метод draw? Хотелось бы какую-то функцию DrawShapes(список Shape), W>которая рисует список фигур, но как эта функция будет знать какую фигуру как рисовать? Вводить в фигуры id и делать кучу if? W>Или есть способ получше?
Я бы рекомендовал задуматься над целесообразностью разведения такого зоопарка, ибо любую фигуру можно описать одним классом Spline.