вторник, 31 августа 2010 г.

Реализация шаблона MVC на примере игры типа Packman – Часть 1

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

Для начала определимся, что такое паттерны программирования для чего они нужны, а потом подробнее рассмотрим паттерн Model-View-Controller.

Теория

Понятие шаблона проектирования

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

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

С помощью шаблонов проектирования описываются и формализуются типовые задачи и их решения. В результате опыт, который нарабатывается с большим трудом, становится доступным широкому сообществу программистов. Шаблоны это не просто какая-то теория, это в первую очередь практика, практика программистов, которые уже решали похожие задачи и нашли ценой проб и ошибок, размышлений и обмена мнениями то, как нужно проектировать приложение или его отдельные части в конкретном случае для поставленных требований. В английском языке есть такой термин “best practice”(перевод: “лучшие методы” или “наилучшие решения”) я считаю, что это выражение лучшим образом описывает суть паттернов. Знаменитый приверженец шаблонов Мартин Фаулер говорит, что он открывает шаблоны, а не создает их. Поэтому многие шаблоны у программистов вызывают чувство “дежа вю” — ведь часто бывает, что вы используете некоторые методы при проектировании своего приложения и просто не знаете, что они вынесены в шаблоны.

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

Вообще если обратиться к истории, то первые шаблоны проектирования для языка программирования Smalltalk представили в 1987 г. Кэнт Бэк и Вард Каннингем. Но это было только началом, настоящее признание в мире программирования они получили после публикации книги «Приемы объектно-ориентированного проектирования. Паттерны проектирования», написанной программистами Э. Гаммом, Р. Хелмом, Р. Джонсоном и Дж. Влиссидесом. В ней были представлены 23 шаблона, ставших сейчас основными. Данная работа дала толчок к изучению паттернов программистами. Издание «банды четырех» (так в шутку прозвали авторов книги) до сих пор остается одним из ИТ-бестселлеров, и его постоянно публикуют.

Краткий обзор основных паттернов

Рассмотрим кратко основные первые 23 паттерна, с которых все началось и которые сейчас должен знать каждый программист. Они разделены на три группы (для каждого паттерна приводится английское название из книги GoF и устоявшийся русский перевод). Краткое описание и разделение на группы описано в статье Олега Фёдорова в журнале PC Magazine.

«Порождающие шаблоны». В этой группе собраны паттерны, описывающие разные способы создания объектов. Прежде всего, это «Фабричный метод» (Factory Method), прием определения интерфейса создания объектов, при этом выбранный класс воплощается в подклассах. Шаблон «Абстрактная фабрика» (Abstract Factory) определяет интерфейс для создания семейств, связанных между собой или независимых объектов, конкретные классы которых неизвестны. С помощью шаблона «Строитель» (Builder) можно отделить процесс конструирования сложного объекта от его конкретного представления и при этом использовать один и тот же процесс для создания различных представлений. «Прототип» (Prototype) описывает виды разрабатываемых объектов с помощью прототипа и создает новые путем его копирования. Применение шаблона «Одиночка» (Singleton) гарантирует, что некоторый класс может иметь только один экземпляр (и предоставляет глобальную точку доступа к нему).

«Структурные паттерны». В этой группе собраны паттерны, которые позволяют менять структуру взаимодействия классов. «Адаптер» (Adapter) позволяет адаптировать интерфейс класса к конкретной ситуации, средствами шаблона «Мост» (Bridge) можно отделить интерфейс класса и его реализацию, «Компоновщик» (Composite) объединяет объекты в древовидную структуру для представления иерархии от частного к целому. Компоновщик позволяет клиентам единообразно обращаться к отдельным объектам и группам объектов. Паттерн «Оформитель» (Decorator, также известен какWrapper, «Оболочка») позволяет динамически добавлять новое поведение к объекту, «Фасад» (Facade)— скрыть сложность системы путем сведения всех возможных внешних вызовов к одному объекту, делегирующему их соответствующим объектам системы. Шаблон «Приспособленец» (Flyweight) используется для облегчения работы с большим числом мелких объектов, а «Заместитель» (Proxy)— контролировать доступ к объекту, перехватывая все вызовы к нему.

В группе «Паттерны поведения» собраны шаблоны, ответственные за реализацию поведения объектов. «Цепочка ответов» (Chain of Response) позволяет пропустить запрос через цепочку объектов, «Команда» (Command) инкапсулирует команду в объект, «Интерпретатор» (Interpreter) позволяет создать общее декларативное решение для часто изменяющихся условий задачи. В шаблоне «Итератор» (Iterator) организуется последовательный доступ к коллекции, «Посредник» (Mediator) определяет упрощенный механизм взаимодействия классов, «Напоминание» (Memento) задает принципы, позволяющие записывать и восстанавливать внутреннее состояние объекта. Средствами шаблона «Наблюдатель» (Observer) можно оповещать об изменениях множества объектов, «Состояние» (State)— менять поведение объекта при изменении его состояния. «Стратегия» (Strategy) инкапсулирует алгоритм внутри класса. Паттерн «Шаблонный метод» (Template Method) выделяет конкретные шаги в алгоритме и опирается на подклассы для их реализации. Средствами паттерна «Посетитель» (Visitor) в класс добавляются новые операции без его изменения.

Вот мы очень кратко ознакомились со всеми основными паттернами и определились с тем, что паттерны это очень полезная вещь, и каждый хороший программист должен знать и использовать шаблоны проектирования при разработке своих приложений. Для хорошего изучения паттернов программирования я советую прочитать книгу “Приемы объектно-ориентированного проектирования. Паттерны проектирования”, написанную Э. Гамма, Р. Хелм, Р. Джонсон, Дж. Влиссидес. В данной книге описываются простые и изящные решения типичных задач, возникающих в объектно-ориентированном проектировании. Авторы излагают принципы использования паттернов проектирования и приводят их каталог. Таким образом, книга одновременно решает две задачи. Во-первых, в ней демонстрируется роль паттернов в создании архитектуры сложных систем. Во-вторых, применяя содержащиеся в справочнике паттерны, вы сможете с легкостью разрабатывать собственные приложения. Издание предназначено как для профессиональных разработчиков, так и для программистов, осваивающих объектно-ориентированное проектирование. Как показывает практика, чтобы понять и осознать всё что написано в данной книге, прочитать её придётся не один раз. А для лучшего освоения нужно попробовать применить шаблоны проектирования на практике для решения хотя бы небольших задач. Так далее я попробую применить шаблон MVC (Model-View-Controller) при написании игры типа Packman. Определимся сначала с тем что нам нужно сделать. Т.е. приведу техническое задание.

Практика

Концептуальная модель

Игра состоит из поля с расставленными на нем стенами, на котором взаимодействуют несколько сущностей:

  1. Русский колобок. Начинает постоянное движение при старте игры и управляется пользователем с помощью клавиатуры. Игра заканчивается при столкновении колобка с вражеским танком.
  2. Вражеский танк. Может присутствовать в количестве нескольких штук (по умолчанию 5). Начинает постоянное движение при старте игры. На каждом шаге может изменить движение путем случайного выбора направления следующего шага. При столкновении двух танков каждый из них разворачивается на 180 градусов и продолжает движение
  3. Наливное яблоко. Может присутствовать в количестве нескольких штук (по умолчанию 5). Случайным образом расставляются в свободные для прохода клетки поля при старте игры. При наезде танка на яблоко ничего не происходит – танк проходит через клетку с яблоком и продолжает двигаться дальше. При столкновении с яблоком колобка - яблоко исчезает (съедается), общий счет игры увеличивается на 1 очко, и яблоко снова появляется в случайно выбранном месте на игровом поле.

Объекты при движении должны смещаться на пиксель для создания эффекта плавного перемещения.

Проектирование

1. Уровень представления данных и логики приложения

Приложению в качестве параметров запуска должны передаваться следующие параметры:

  1. Размеры игрового поля (ширина и высота)
  2. Количество танков на поле
  3. Количество яблок на поле
  4. Скорость передвижения объектов (задается для всех объектов сразу).

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

  1. Игровое поле, на котором будут прорисовываться перечисленные концептуальной модели объекты.
  2. Кнопка New Game, при нажатии на которую будет начинаться новая игра.

При старте игры поле представляет собой пространство, со стенами и объектами, расположенными на нем примерно следующим образом:

clip_image002

Рис. 1. Пример интерфейса игры типа Packman

2. Паттерны

При проектировании приложения следует использовать паттерн MVC (Model View Controller).

Конструирование

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

Изображение сущностей должно быть реализовано с помощью классов PackmanView и TankView соответственно.

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

Дополнительные задания
  1. Реализовать «прозрачные стены» для игрового поля – т.е. когда танк подходит к границе поля, он не отходит в сторону, а выходит с противоположной стороны поля, то же самое происходит с колобком.
  2. Реализовать второе окно в качестве альтернативного view для игрового поля. Оно будет представлять из себя frame с таблицей со следующими полями:
    - название объекта
    - координаты в следующем виде: (X, Y)
  3. Сделать возможность расстрела колобком танков

В следующих статьях я предложу свою реализацию описанного задания, а также я подробно опишу паттерн проектирования MVC в классическом его понимании. А вы можете попробовать написать сами и потом сравнить с моим решением. Я не буду реализовывать дополнительное задание, и описывать это в своих статьях, так как целью решения этого задания является не создание интересной навороченной игры, а изучение и применение шаблона MVC, событийной модели приложений, применение принципов ООП, использование интерфейсов и шаблонных типов. Также нужно попытаться сделать приложение достаточно производительным, чтобы игра не тормозила на стандартных конфигурациях компьютеров, что легко может быть т.к. по заданию каждая сущность имеет свой поток. Также хочу отметить, что создавать приложение я буду на C# c использованием технологии Windows Form. Все исходники данной игры можно будет скачать по ссылке, приведённой в последней статье.

2 комментария:

  1. Вы пишите: "Все исходники данной игры можно будет скачать по ссылке, приведённой в последней статье".
    Хотел запустить, посмотреть как работает, но не нашел ссылки(

    ОтветитьУдалить
    Ответы
    1. Исходники к статье можно скачать по ссылке:
      https://skydrive.live.com/redir.aspx?cid=464a2efc7fa658f7&resid=464A2EFC7FA658F7!662&parid=464A2EFC7FA658F7!114&authkey=!AGVRDxZI9FH3k80

      Удалить