Как наилучшим образом применить принципы ООП к играм и другим приложениям с графическим интерфейсом, управляемым вводом? - PullRequest
2 голосов
/ 19 декабря 2008

Всякий раз, когда я пытаюсь написать графические программы (будь то игра или действительно любое приложение с графическим интерфейсом), я всегда получаю один или два бога-класса со слишком большим количеством методов (и длинных методов тоже), и каждый класс имеет слишком много обязанности. Я делаю графику одновременно с вычислениями и логикой, и я чувствую, что это действительно плохой способ организовать мой код. Я хочу стать лучше в организации своего кода и распределении обязанностей между различными классами. Вот пример, с которого я хотел бы начать - я хочу написать клон Minesweeper, просто для практики и попытаться улучшить свои навыки разработки программного обеспечения. Как бы я сделал этот красивый и объектно-ориентированный? Ради обсуждения, давайте просто скажем, что я использую Java (потому что я, вероятно, буду, или это или C #). Вот некоторые вещи, о которых я бы подумал:

  • должна ли каждая плитка наследоваться от JButton или JComponent и обрабатывать сам рисунок?
  • или плитки должны храниться только как неграфический объект MinesweeperTile, а некоторые другие классы обрабатывают их рисование?
  • является ли таймер обратного отсчета для 8-сегментного дисплея (по крайней мере, до Vista) отдельным классом, который обрабатывает сам рисунок?
  • когда пользователь щелкает мышью, есть ли у плиток прослушиватели событий мыши или какой-то другой метод обнаружения столкновений циклически проходит по плиткам и проверяет каждую из них на предмет попадания?

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


edit: Полагаю, я должен добавить, что я знаком с MVC, и я изначально собирался включить это в свой вопрос, но, думаю, я не хотел вводить себя в MVC, если это не обязательно то, что мне нужно , Я искал темы на MVC с приложениями с графическим интерфейсом, но не нашел ничего, что отвечало бы на мой конкретный вопрос.


edit2: Спасибо всем, кто ответил. Я хотел бы принять более одного ответа ..

Ответы [ 6 ]

8 голосов
/ 19 декабря 2008

Вот простой (но эффективный) дизайн ОО, с которого можно начать:

Сначала создайте объект Game, представляющий собой чистый код Java / C #. Без пользовательского интерфейса или чего-либо еще для конкретной платформы. Объект Game обрабатывает объект Board и объект Player. Объект Board управляет несколькими объектами Tile (где находятся мины). Объект Player отслеживает «Число ходов», «Счет» и т. Д. Вам также понадобится объект Timer, чтобы отслеживать время игры.

Затем создайте отдельный объект пользовательского интерфейса, который ничего не знает об объекте Game. Он полностью автономен и полностью зависит от платформы. Он имеет свой собственный UIBoard, UITile, UITimer и т. Д., И ему можно рассказать, как изменить свои состояния. Объект пользовательского интерфейса отвечает за интерфейс пользователя (вывод на экран / звук и ввод от пользователя).

И, наконец, добавьте объект Application верхнего уровня, который считывает ввод из объекта пользовательского интерфейса, сообщает Game, что делать на основе ввода, уведомляет Game об изменениях состояния, а затем поворачивается и сообщает UI, как обновить сам по себе.

Это (кстати) адаптация паттерна MVP (модель, вид, презентатор). И (кстати) шаблон MVP на самом деле является просто специализацией шаблона Mediator. И (еще один, кстати) шаблон MVP - это в основном шаблон MVC (модель, представление, управление), в котором представление НЕ имеет доступа к модели. Что является большим улучшением ИМХО.

Веселись!

2 голосов
/ 19 декабря 2008

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

  • Каждая плитка должна наследовать от чего-то и обрабатывать сам рисунок. Кнопка кажется лучшим решением, потому что она уже имеет встроенную функцию рисования кнопки (нажатой, неотжатой и т. Д.).
  • Каждая плитка также должна знать о своих соседях. У вас будет восемь указателей на каждого из его восьми соседей, и, конечно, они будут равны нулю, если соседа нет. Когда он идет на рисование, он запрашивает функцию каждого соседа IsMine() и отображает счетчик.
  • Если ни один из его соседей не является моим, он затем вернется к методу Reveal() каждого соседа.
  • Для 7-сегментного дисплея каждая цифра является собственным классом, который обрабатывает рисование. Затем я бы создал класс CountdownSegmentDigit, который наследуется от этого класса, но имеет дополнительные функции, а именно методы CountDown(), Set() и Reset(), а также событие HitZero. Затем сам таймер дисплея представляет собой набор этих цифр, подключенных для распространения оставшихся нулей. Затем имейте Timer в классе таймера, который тикает каждую секунду и отсчитывает самую правую цифру.
  • Когда пользователь нажимает, см. Выше. Сам тайл будет обрабатывать щелчок мыши (в конце концов, это кнопка) и вызывать метод Reveal(). Если это мина, она запустит событие MineExploded, которое будет слушать ваша основная форма.

Для меня, когда я думаю о том, как инкапсулировать объекты, это помогает представить его как производственный процесс для физических частей. Спросите себя: «Как я могу спроектировать эту систему, чтобы она могла быть наиболее эффективно построена и использована повторно?» Подумайте и о будущих возможностях повторного использования. Помните, что процесс сборки берет маленькие кусочки и собирает их в большие и большие куски, пока весь объект не будет построен. Каждый бит должен быть максимально независимым и обрабатывать свою собственную логику, но при необходимости иметь возможность общаться с внешним миром.

Возьмите 7-сегментный бит дисплея, вы можете использовать его позже, который не обратный отсчет. Скажем, вы хотите спидометр в машине или что-то. У вас уже будут цифры, которые вы можете соединить вместе. (Представьте себе аппаратное обеспечение: стандартные 7-сегментные дисплеи, которые ничего не делают, кроме как загораются. Затем вы подключаете к ним контроллер, и они получают функциональность.)

На самом деле, если вы думаете достаточно усердно, вам может понадобиться CountUp() функциональность тоже. И аргумент события в HitZero, чтобы сказать, был ли это путем подсчета вверх или вниз. Но вы можете подождать до тех пор, пока не добавите эту функцию, когда вам это нужно. Вот где сияет наследство: наследуй за CountDownDigit и сделай CountUpOrDownDigit.

Думая о том, как я мог бы проектировать его в аппаратном обеспечении, вы можете захотеть спроектировать каждую цифру, чтобы она знала о своих соседях и подсчитывала их в порядке увеличения или уменьшения, когда это необходимо. Пусть они запомнят максимальное значение (помните, 60 секунд до минуты, а не 100), поэтому, когда они переворачивают 0, они сбрасываются соответствующим образом. Есть мир возможностей.

2 голосов
/ 19 декабря 2008

используйте инфраструктуру MVC, которая выполняет всю сложную организационную работу за вас. Есть множество тем по MVC-фреймворку на SO.

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

1 голос
/ 19 декабря 2008

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

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

Интерфейс пользователя начинается с определения последовательности форм или экранов. Идея состоит в том, что для каждой формы или экрана вы создаете интерфейс, который определяет, как контроллер UI будет взаимодействовать с ним. В общем, для каждой формы или экрана существует один класс UI Controller.

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

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

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

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

Мне не нравится архитектура MVC, так как я чувствую, что она плохо справляется с проблемами GUI. Я предпочитаю использовать Контролирующий контроллер, который вы можете прочитать о здесь . Причина в том, что я считаю, что автоматизированные тесты являются одним из наиболее важных инструментов, которые у вас есть. Чем больше вы можете автоматизировать, тем лучше для вас. Наблюдающий шаблон презентатора делает формы тонкой оболочкой, поэтому очень мало того, что не может быть проверено автоматически.

0 голосов
/ 19 декабря 2008

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

Нет способа ответить на ваш вопрос как таковой, но здесь мы идем.

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

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

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