Модель реального мира или правильный мир ООП? Или оба? - PullRequest
8 голосов
/ 30 августа 2009

Я пишу игру, в которой управляемый мышью объект контроллера нажимает на объект игрока, чтобы он что-то сделал.

Существует 2 способа инициирования взаимодействия между мышью и игроком:

  • Контроллер вызывает функцию игрока:
    Контроллер прослушивает события мыши. Когда щелчок мыши происходит в любом месте на экране, контроллер выполняет поиск всех объектов в точке, по которой щелкнули. Если один из объектов является объектом игрока, и его свойство «clickable» имеет значение true, тогда вызовите его соответствующую функцию.
  • Игрок вызывает функцию контроллера:
    Игрок слушает события мыши. Когда щелчок мыши происходит на плеере, а собственное свойство "clickable" игрока имеет значение true, тогда вызовите соответствующую функцию контроллера.

Моя дилемма здесь в том, что первый вариант кажется более интуитивным с тем, как я представляю себе сценарий, происходящий в реальном мире, но второй вариант кажется более интуитивным при правильном объектно-ориентированном проектировании, поскольку он не требует изучения свойств другого объекта. , что в некоторой степени нарушает инкапсуляцию (контроллер должен заглянуть в плеер, чтобы прочесть его свойство «clickable»). Кроме того, второй вариант кажется встроенным в шаблон проектирования «Контроллер».

Это всегда борьба за меня - могу ли я бросить вызов правильному объектно-ориентированному дизайну (например, вариант 1) или я использую реализацию, которая кажется противоречащей действительности (например, вариант 2)?

Я надеюсь, что мне не хватает какого-то промежуточного положения.

Ответы [ 11 ]

8 голосов
/ 30 августа 2009

Это всегда борьба за меня - могу ли я бросить вызов правильному объектно-ориентированному дизайну (например, вариант 1) или я использую реализацию, которая кажется противоречащей действительности (например, вариант 2)?

Я не думаю, что целью объектной ориентации является моделирование реального мира.

Я думаю, что (?) Причина, по которой ОО-модель часто следует модели реального мира, заключается в том, что реальный мир не сильно меняется: и поэтому выбор реального мира в качестве модели означает, что программное обеспечение не сильно изменится, т.е. будет недорогим в обслуживании.

Преданность реальному миру сама по себе не является целью проектирования: вместо этого вы должны стремиться к проектам, которые максимизируют другие показатели, например, простота.

«Объекты» в «объектной ориентации» являются программными объектами, необязательно объектами реального мира.

5 голосов
/ 30 августа 2009

Почему бы не перейти к варианту 3, который аналогичен варианту 1?

  • Контроллер вызывает функцию игрока:
    Контроллер прослушивает события мыши. Когда щелчок мыши происходит в любом месте на экране, контроллер выполняет поиск всех объектов в точке, по которой щелкнули. Если один из объектов реализует IClickable или наследует Clickable (или что-либо еще), тогда вызовите его соответствующую функцию (или запустите событие, в зависимости от того, что подходит).
4 голосов
/ 30 августа 2009

Второй метод - явно более идиоматический способ работы с Flash. AS3 имеет модель событий, встроенную прямо в EventDispatcher, и все DisplayObjects наследуются от нее. Это означает, что любой Bitmap, Sprite или MovieClip сразу узнает, нажали ли они.

Думайте о Flash Player как о вашем контроллере. Когда я делаю MVC во Flash, я почти никогда не пишу контроллер, потому что Flash Player делает это за вас. Вы теряете циклы, определяя, на что нажимали, когда Flash Player уже знает.

var s:Sprite = new Sprite();
s.addEventListener(MouseEvent.CLICK, handleMouseClick);
function handleMouseClick(event:MouseEvent):void
{
    // do what you want when s is clicked
}

Я бы, вероятно, не получал прямой доступ к контроллеру из спрайта (возможно, в виде). Вместо этого я бы отправил событие (возможно, определенное пользовательское событие, которое соответствует обстоятельствам) Примите решение о том, сколько раз за кадр что-то происходит. Реакция на взаимодействие с пользователем (например, щелчок мышью) обычно дает вам свободу не беспокоиться о накладных расходах в системах событий.

Наконец - причина, по которой я предполагаю, что это не имеет никакого отношения к некоторой концепции OOD или OOP Башни Слоновой Кости. Такие принципы существуют, чтобы помочь вам не сдерживать вас. Когда дело доходит до прагматики, используйте самое простое решение, которое не вызовет у вас головной боли. Иногда это означает выполнение ООП, иногда это означает функциональность, иногда это означает императив.

4 голосов
/ 30 августа 2009

Концепция MVC является хорошей практикой в ​​целом и все же должна использоваться в игровых проектах в качестве общего принципа. Но, учитывая интерактивный характер игрового интерфейса, вы также должны обратить внимание на архитектуру, основанную на событиях. MVC не противоречит архитектуре, основанной на событиях, если она разработана тщательно. (PureMVC в качестве примера.)

Я предлагаю вам использовать наблюдаемый шаблон для всех экранных объектов, чтобы все они могли прослушивать / запускать события. Это избавит вас от головной боли в дальнейшем. Когда ваша кодовая база станет более сложной, вам, в конечном итоге, придется использовать больше методов развязки, таких как описанный вами вариант 2. Также поможет шаблон посредника .

Edit:

Шаблон посредника - это, как правило, хороший способ организации событий уровня приложения.

Вот блог об использовании MVC, событий и посредников в игровом программировании:

http://ezide.com/games/writing-games.html

2 голосов
/ 17 сентября 2009

Это всегда борьба за меня - могу ли я бросить вызов правильному объектно-ориентированному дизайну (например, вариант 1) или я использую реализацию, которая кажется противоречащей действительности (например, вариант 2)?

Реальность может быть хорошей отправной точкой для разработки или развития дизайна, но всегда ошибочно моделировать ОО дизайн в реальности.

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

Пример 1

В математике квадрат является прямоугольником. Похоже, хорошая идея унаследовать класс Square от класса Rectangle. Вы делаете это, и это приводит к гибели. Зачем? Потому что ожидание или убеждение клиента было нарушено. Ширина и высота могут варьироваться независимо, но Square нарушает этот контракт. У меня был прямоугольник измерения (10, 10), и я установил ширину 20. Теперь я думаю У меня есть прямоугольник измерения (20, 10), но фактический экземпляр - это квадратный экземпляр с размерами ( 20, 20) и я, клиент, ожидаю настоящего большого сюрприза. Так что теперь у нас есть нарушение Принципа Наименьшего Сюрприза.

Теперь у вас есть ошибочное поведение, которое приводит к усложнению клиентского кода, как будто необходимы операторы для обхода ошибочного поведения. Вы также можете найти свой клиентский код, требующий RTTI, чтобы обойти ошибочное поведение, протестировав конкретные типы (у меня есть ссылка на Rectange, но я должен проверить, действительно ли это экземпляр Square).

Пример 2

В реальной жизни животные могут быть плотоядными или травоядными животными. В реальной жизни мясо и овощи - это виды пищи. Таким образом, вы можете подумать, что было бы неплохо иметь класс Animal в качестве родительского класса для разных типов животных. Вы также думаете, что было бы неплохо иметь родительский класс FoodType для класса Meat и класса Vegetable. Наконец, у вас есть в классе Animal sport метод eat (), , который принимает FoodType в качестве формального аргумента.

Все компилируется, проходит статический анализ и ссылки. Вы запускаете свою программу. Что происходит во время выполнения, когда подтип Animal, скажем, травоядный, получает FoodType, который является экземпляром класса Meat? Добро пожаловать в мир ковариации и контравариации. Это проблема для многих языков программирования. Это также интересная и сложная проблема для языковых дизайнеров.

В заключение ...

Так что ты делаешь? Вы начинаете с вашей проблемной области, ваших пользовательских историй, ваших вариантов использования и ваших требований. Пусть они управляют дизайном. Позвольте им помочь вам обнаружить сущности, которые вам нужно моделировать в классы и интерфейсы. Когда вы это сделаете, вы обнаружите, что конечный результат не основан на реальности.

Выезд Анализ образцов Мартина Фаулера. Там вы увидите, что движет его объектно-ориентированным дизайном. В основном это зависит от того, как его клиенты (медицинские работники, финансовые люди и т. Д.) Выполняют свои повседневные задачи. Оно пересекается с реальностью, но не основано и не обусловлено действительностью.

2 голосов
/ 09 сентября 2009

Согласно Применение UML и шаблонов (Крейг Ларман) пользовательский интерфейс (события мыши) никогда не должен взаимодействовать с классами вашего приложения, то есть пользовательский интерфейс никогда не должен напрямую управлять бизнес-логикой.

Вместо этого один или несколько контроллеров должны быть определены в качестве промежуточного уровня для пользовательского интерфейса, поэтому вариант 1 фактически следует хорошему объектно-ориентированному подходу.

Если подумать, имеет смысл объединить как можно меньше классов с пользовательским интерфейсом, чтобы сделать бизнес-логику максимально независимой от пользовательского интерфейса.

1 голос
/ 17 сентября 2009

Другой подход заключается в создании интерфейса IClickHandler. Все объекты, которые регистрируются на клики, делают это, передавая IClickHandler контроллеру. Когда объект щелкается, контроллер вызывает метод clicked () для зарегистрированного IClickHandler. IClickHandler может переадресовывать или не перенаправлять вызов зарегистрированному в нем методу. Теперь ни ваш контроллер, ни ваш объект не принимают решения о том, действительно ли на упомянутый объект был нажат объект.

IClickHandler также может быть выбран на основе других критериев (IOW объект не выбирает сам IClickHandler при регистрации, какой-то другой алгоритм выбирает его). Хорошим примером будет то, что всем объектам NPC назначается IClickHandler, который перенаправляет щелчки, а всем деревьям - IClickHandler, который не перенаправляет щелчки.

Как минимум у вас может быть 3 обработчика, которые реализуют интерфейс: AlwaysClickable, NeverClickable, ToggledClickable

Просто имейте в виду, что вышесказанное имеет больше подвижных частей, а также небольшой удар по производительности, но дает вам большую гибкость (вам решать, стоит ли эта гибкость дополнительной сложности).

Также обратите внимание, что лучше не просто придерживаться принципов любого типа. Делайте то, что лучше всего, с учетом обстоятельств, когда вы пишете код. Если вы пишете клон Tetris, тот факт, что Вариант 1 «нарушает» принципы ООП, совершенно не имеет значения, вы никогда не поймете преимущества строгого ООП в таком простом проекте.

1 голос
/ 11 сентября 2009

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

Это не должно помешать вам найти другие решения. Но старайтесь всегда придерживаться языковой идиомы. ООП не приводит к реальной жизни в отношениях 1: 1, и при этом он не особенно хорош в эмуляции. Наглядным примером являются классические прямоугольные и квадратные объектные отношения, о которых вы, вероятно, уже все знаете. В реальной жизни квадрат - это прямоугольник. В ООП (или, по крайней мере, в правильном ООП) это отношение не очень хорошо переходит в основанное на основе отношение. Таким образом, вы чувствуете необходимость оторваться от реальной эмуляции, потому что языковая идиома говорит выше. Либо так, либо мир боли, когда вы начинаете серьезно реализовывать как прямоугольник, так и квадрат, или позже хотите внести в них изменения.

1 голос
/ 09 сентября 2009

Я бы не согласился с вашей оценкой двух вариантов. Вариант 2 хуже ОО, поскольку он тесно связывает объект Player с конкретной реализацией пользовательского интерфейса. Что произойдет, если вы захотите повторно использовать свой класс Player где-нибудь, где он находится вне экрана или используется интерфейс без мыши?

Вариант 1 все еще можно улучшить, хотя. Предыдущее предложение об использовании интерфейса iClickable или суперкласса Clickable является огромным улучшением, поскольку оно позволяет реализовывать различные типы интерактивных объектов (не только Player), не предоставляя контроллеру огромный список «Является ли объект этим классом? это тот класс? пройти.

Ваше основное возражение против варианта 1 заключается в том, что он проверяет свойство «кликабельности» игрока, которое, по вашему мнению, нарушает инкапсуляцию. Это не так. Он проверяет свойство, которое определено как часть открытого интерфейса проигрывателя. Это ничем не отличается от вызова метода из открытого интерфейса.

Да, я понимаю, что в этот момент свойство "кликабельности" реализуется таким образом, что это простой метод получения, который не делает ничего, кроме запроса внутреннего состояния проигрывателя, но он не имеет быть . Завтра свойство может быть переопределено, чтобы определить его возвращаемое значение совершенно другим способом без ссылки на внутреннее состояние, и, пока оно все еще возвращает логическое значение (т. Е. Открытый интерфейс остается прежним), код, использующий Player.clickable, будет все еще работает просто отлично. В этом разница между собственностью и прямой проверкой внутреннего состояния - и это огромная разница.

Если это все еще вызывает у вас беспокойство, достаточно просто исключить проверку контроллера Player.clickable: просто отправьте событие click каждому объекту под мышью, который реализует iClickable / идет от Clickable. Если при получении щелчка объект находится в состоянии, в котором нельзя нажать, он может просто проигнорировать его.

1 голос
/ 30 августа 2009

я думаю, важно отделить входную логику от логики приложения ... подход заключается в преобразовании входных событий (будь то пользовательский ввод или некоторые данные, поступающие через сокеты / локальные соединения и т. Д.) В прикладные события (события в абстрактное значение слова) ... это преобразование сделано тем, что я бы назвал "фронт-контроллерами" из-за отсутствия лучшего термина ...

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

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

так что да, это определенно версия 1 ... в этом случае, переднему контроллеру для мышиных входов нужно только знать список отображения и иметь логику о том, когда отправлять какое событие ... и контроллер приложения должен быть может обрабатывать какое-то событие PlayerEvent.SELECT в этом случае ... (если позже вы решите, использовать какой-то режим обучения или что-то еще, вы можете просто перемещаться по фальшивой мышке и отправлять это событие в случае фальсификации щелкает мышкой, или вы можете просто повторить все в какой-то форме воспроизведения, или вы можете использовать это для записи макросов, когда речь идет не об играх ... просто чтобы указать на некоторые сценарии, где это разделение полезно)

надеюсь, это поможет ...;)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...