Amphilohiy пишет:
Кажется, то что я имел в виду, называют Декоратором.
Скорее
состояние (автомат). Так же рекомендуется ознакомится с
стратегия. А декорация это обвертка функции. Похожим механизмом пользуются скриптеры мукера (тот же alias в руби), только из-за некоторых тонкостей не приходится делать отдельный класс для этого.
Не знаю, что есть alias, увы. А шаблон "Стратегия" тут не подходит, потому что количество классов, которые нужно описать, это комбинационный взрыв исходных компонентов. "Состояние" уже что-то более близкое, но для того чтобы выполнять по-компонентно все эти "частицы поведений", нужно иметь сразу много состояний, или постоянно их на месте переключать, что кажется мне странным. В общем, или я туплю, или это всё ещё другой паттерн, который решает что-то другое.
А про go - забавно, однако.
EvilCat пишет:
Вызов одного из стандартных событий, которые есть у каждого объекта? Это полиморфизм, конечно, но не тот, который позволяет воспользоваться хорошими сторонами ООП.
В данном случае прошу прощения уже я, но всё-таки - "вам ООП или игры прототипировать/делать"? Да, в Гамаке оно кривее чем хотелось бы, но это его слабое место, а сильное - в функциональной заточенности на 2Д-игры самых разных жанров. Для меня лично Гамак победил, потому что суммарная целесообразность использования этой хорошей IDE с этим... особенным языком - выше, чем у других испытанных мной средств для разработки игр - Scratch, Multimedia Fusion, Stencyl, Construct, тот самый Unity3D, и там ещё что-то, чего уже и не упомню. Графическое программирование из блоков пока что доказало свою несостоятельность в таком виде, в каком оно есть, а из попыток гибридных систем блоки+код Гамак опять же самая удачная. Хотя эти блоки я давно не использую, опять же.
(весь этот абзац о том, что я не думаю о степени ООПшности, и это нормально, пока я достигаю поставленной цели)
EvilCat пишет:
Например, есть персонаж (объект), который может взять в руку оружие (объект). Он может взять любое оружие и нажать крючок, но каждое оружие делает своё: одно стреляет пулей, другое показывает флажок с надписью "Бах", третье лечит самого персонажа. Логично в таком деле сделать каждый тип оружия потомком класса "Оружие" (или реализатором интерфейса "Крючок") с методом "нажать", а оружие уж пусть само разбирается.
Логично, согласен. Хотя оружие, которое лечит, немного смущает - эта штука, если и является оружием, то лечение должна производить, скажем, имплементацией интерфейса IHealingItem, или что-то в таком духе. Тот факт, что оружие само должно разбираться (как и все остальные объекты) - неоспорим, но я против него и не шёл в предложенном выше способе раскладывания поведений по объектам - предполагается, что все поведения известны ровно тем объектам, которым они конечном счёте нужны, причём часть этих поведений используется составными объектами, методом вызова событий из "менее составных".
EvilCat пишет:
Делать из оружия божественный класс (автомат, стратегию?) "если я пистолет, то... если я клоунский пистолет, то... если я шприц, то..." - адово, хотя, к примеру, в VBA (кстати, тоже статически типизированный язык с полиморфизмом, но без наследования) это иногда сделать проще и быстрее, чем что-либо ещё - для прототипа годится.
Итого, всё зависит от задачи. Если отличие каждого типа пистолета описывается парой строчек, а пистолетов десятки, то это проще потом поддерживать, чем если создать кучу наследников-пере-до-определителей.
EvilCat пишет:
Договариваться "слушай, короче, нажатие крючка - это будет по событию ev_step)" - нецелевое применение системы. Представь ситуацию, что у нас есть несколько типов пистолетов, каждый из которых выпускает пулю, но также делает кое-что ещё - огнестрельный бахает, цифровой пишет в лог, шпионский выпускает также пулю в обратном направлении в не ожидающего врага... А один безумный пистолет вместо пули выстреливает другим пистолетом случайным пистолетом, который тут же тоже выстреливает. А ещё один срабатывает, только если с нажатием крючка крикнуть "Огонь!". Это целая куча поведений, часть из которых наследуется от предыдущего члена цепочки, часть - модифицирует сделанное предыдущим членом цепочки, а часть - добавляет что-то своё или даже переписывает произошедшее. Если бы мы договорились использовать строго одно событие, например, "пользовательское событие № 1" как вызов метода "нажать", нам бы быстро пришлось израсходовать весь запас пользовательских событий на наследование под-методов, которые мы бы просто применяли в нормальном ООП; или вписаться в копипасту.
Мне уже нравится такая иерархия пистолетов! Ну хорошо, забыли то что выше, тогда вот что я предлагаю:
1) Описать иерархию классов:
Все поведения описаны только в User Event 0 каждого объекта.
Название | Родитель | Смысл |
o_pistol | Нет | "Абстрактный" (такого аттрибута в GML нет, это он по использованию по факту абстрактный). Он существует сугубо для того чтобы конструкция with o_pistol захватывала в контекст сразу всех его наследников. |
o_pistol_firearm | o_pistol | Описано поведение выпускания пули. |
o_pistol_boom | o_pistol_firearm | event_inherited() запускает поведение по выпуску пули, а ещё делаем sound_play(...). |
o_pistol_digital | o_pistol | Пишем в лог. Не понял, надо ли выпускать пулю - если да, то event_perform_object(o_pistol_firearm, ev_user, 0) |
o_pistol_backfire | o_pistol_firearm | event_inherited() запускает поведение по выпуску пули, и тут же добавляем второй выпуск, только назад. |
o_pistol_pistolspawner | o_pistol | Делает p=instance_create(x, y, choose(список пистолетов либо вместо этого вызов скрипта выбора пистолетов)); with p event_user(0). Это создаёт пистолет и заставляет его выполнить его "главную функцию", которую мы своевольно приравняли к Пользовательскому Событию №0, ибо почему нет? |
2) Выстреливание только если вместе с нажатием выкрикнуто "Огонь", реализовать через добавление в User Event 0 самого первого объекта o_pistol строку что-то вроде if works_with_shout && shout.
И кстати, я по ходу дольше расписывал, как бы я это сделал, чем я бы это по факту в ГМ сделал! А если прототип/игра работает, для чего же думать, "ровное" или "нормальное" ли там ООП?
EvilCat пишет:
Это не теоретические рассуждения - я писала экспериментальный прототип, когда столкнулась с этой проблемой и столкнулась с этими трудностями.
Радует, что говорю не с академико-теоретиками (как часто бывает). Не знаю как в VBA, а в GML всё-таки мне кажется не зазорным (не принадлежащим к классу плохих практик ООП) выполнять события других объектов-как-контейнеров-поведений, которые по факту во время выполнения программы никогда и не создаются, а служат чем-то вроде ячеек, куда ложат нужные поведения.