В середине прошлого лета на программерскую рассылку отдела клиентских игр Mail.Ru Group пришло письмо – «Объявлен конкурс на позицию преподавателя курса Java в нашем совместном проекте с МГТУ им. Н. Э. Баумана». Как программист и сотрудник отдела я это письмо тоже получил. Как выпускник физтеха, а в прошлом преподаватель и научный работник, решил откликнуться.
Так для меня началась история работы преподавателем в Технопарке. Собственно об этом (о работе, о Технопарке, о своем курсе и о студентах) я и хочу вам рассказать.
Демотиватор, посвященный обсуждению курса Java *
(*) Демотиватор был сделан на основе записи презентации курса «Углубленное программирование на Java» на выступлении «1 год Технопарка» и получил первое место на закрытом конкурсе демотиваторов.
Задача статьи
Начну с обзора тем и задач статьи. Все свои лекции стараюсь начинать именно так. Итак, я расскажу:
- об истории создания курса и подборе учебных тем и материалов
- о требованиях к слушателям курса и их реальном соответствии этим требованиям
- об особенностях чтения лекций и проведения семинаров в Технопарке
- об архитектуре учебного проекта и практической работе
- о результатах первого семестра
- о защите практической работы, экзамене и билетах
История создания курса
Я ответил на письмо из рассылки. Придумать и прочитать курс по теме своей основной работы — это возможность одновременно получить опыт преподавания, лучше узнать язык программирования, по- новому взглянуть на себя и свою работу, и, вдобавок, заработать денег. Через пару дней мне написал руководитель Технопарка и предложил обсудить проект курса.
Подробно о Технопарке я здесь писать не буду — есть отличная статья руководителя Технопарка Дмитрия Волошина.
Полное название дисциплины курса, который мне предстояло прочесть, звучало так: «Углубленное программирование на Java». Это полугодовой курс для студентов, которые успешно прошли первый семестр обучения в Технопарке. К началу курса у студентов уже должны быть основные знания в области программирования и небольшой опыт разработки на каком-либо языке высокого уровня.
Мне предстояло подготовить материал для 9 лекций и 6 семинаров (по 4 академических часа на каждое занятие). Курс должен был стать частью общей программы по подготовке web-разработчиков и системных архитекторов.
Я был свободен в выборе тем и материалов, мог подготовить любой курс, соответствующий названию. Единственным обязательным условием была практическая направленность курса. То есть студенты должны были не просто прослушать теоретический материал, но и сделать ряд практических работ — например, решить задачи, похожие на те, которые решаем мы сами.
Как я уже писал, я сотрудник отдела разработки клиентских компьютерных игр. Я участвовал в работе над проектом Аллоды Онлайн и сейчас работаю в серверной команде нового проекта SkyForge. Я пишу игры. Точнее, я пишу на Java серверную часть онлайновой игры. Мне надо подготовить курс для студентов, который был бы практически ориентирован на решение проблем, похожих на мои рабочие.
Сформулировав свою задачу таким образом, я понял, какой курс нам нужен. Это должен быть курс, в процессе изучения которого студенты напишут на Java свой игровой сервер.
Это курс по Java? Да. Это практический курс? Да. По моей работе? Да! Начальные условия соблюдены. Можно начинать разработку программы.
Основную работу по подготовке курса я провел сам, но конечно, над многими вопросами я думал не один. Описывая результаты коллективной работы (обсуждений с руководством, коллегами, студентами) я буду писать «мы».
Разработка курса
Я начал работать над программой. Первая сложность, которая сразу бросалась в глаза — сервер бесполезен без клиента. Можно написать «божественный сервер», но, если к нему никто не сможет обратиться, ценность его будет невелика, и писать его будет не интересно. Писать свой клиент, тем более на Java, не было ни желания, ни времени. Решение, в общем-то, тоже бросалось в глаза. В качестве клиента можно использовать браузер. И написать сервер для браузерной игры. По той архитектуре, которую я опишу ниже, с точки зрения сервера разницы в том, какой клиент использовать, нет вообще никакой.
Вторая сложность с подбором материалов была в том, чтобы найти баланс: их должно быть достаточно для создания законченного проекта, но при этом не слишком много, чтобы работу можно было выполнить за отведенное время. За 4 месяца, которые длится курс, студенты должны выполнить работу, равнозначную работе по разработке небольших браузерных и/или социальных игр. Насколько я знаком с такого рода разработками, весь цикл создания подобных игр у команды из 3-4 человек занимает 1-2 месяца. Но в разработке участвуют профессионалы (в большей или меньшей степени), и работают они full-time. А большинство студентов, с которыми нужно было работать мне, опыта разработки не имеют (здесь не важно на каком языке, важно, что нет опыта создания проектов). Вдобавок, кроме моего курса у них 2 курса Технопарка и еще неизвестно сколько курсов МГТУ.
Мы решили, что для достижения наших задач нужно придерживаться ряда правил.
- Все практические задания по разработке кода должны складываться в один проект. Ни одна строка кода не должна быть написана просто для тренировки
- Для задач, не связанных непосредственно с курсом, мы используем сторонние библиотеки.
- Студентов объединяем в группы по 3-4 человека. Одна группа разрабатывает один проект. Оценку за практическую работу получает вся группа.
В результате в программу вошли следующие темы:
- История и особенности языка Java. Платформа Java. Java editions. Сборщик мусора. Сравнение Java и С++. Наследование в Java. Ключевые слова: interface, class, enum, extends, implements.
- class Object и class Class<?>. Классы «обертки» простых типов. Generics. Типизация. Iterator и Iterable. Обзор коллекций Java. Класс Collections. Многопоточность. Processes and Threads. Interface Runnable. class Thread. sleep(), interrupt(), join(). Потоки с точки зрения процессора. Прерывание. Java memory model. Volatile. Критические секции. Synchronization. Мьютексы. Семафоры. Monitor. Проблемы многопоточного доступа к данным. Atomic Variables. Deadlock. wait() и notify().
- Описание проблемы взаимодействия потоков. Задача — взаимодействие потоков Frontend и AccountService. Плюсы решения задачи через два потока. Способы взаимодействия потоков. Concurrent collections. Основная идея взаимодействия потоков через сообщения. MessageSystem — объект для обмена данными. Address и Abonent. Аналогия с почтой. Message — иерархия наследования сообщений. AddressService.
- Организация кода. Проблема циклических зависимостей. Решение циклических зависимостей через интерфейсы. Архитектура сервера. Разбор модулей сервера и их взаимодействия. Игровая механика. Диаграмма взаимодействия модулей игровой механики и фронтенда. Репликация. Примеры игровых механик.
- Виды тестирования. Unit-тестирование. Функциональное тестирование. Нагрузочное тестирование. Практики тестирования. JUnit. Selenium. Анализируемые параметры. GC. Виды GC. Параметры GC. Принципиальное устройство GC.
- Singleton и Context. Events, подписка на события. Callback. Анонимные классы. Random. Работа со временем и датой. Unix time. Locale. Подписка на время. I/O Streams. Потоки байт и потоки символов. Дерево наследования потоков. Файловые потоки. Безопасность закрытия потоков. File. VFS. Примеры кода.
- Сериализация/десериализация. Interface Serializable, transient поля, serialVersionUID. Reflection. Роль reflection в сериализации. Class<T>. Field, Method, Constructor. Пример ReflectionHelper-а. Factory method pattern. SAX и DOM парсеры. Восстановление объекта по XML-файлу. Ресурсы. Использование ReflectionHelper для десериализации ресурсов. Роль ресурсов в игровом сервере. ResourceFactory.
- Шаблон работы с базой. DataSet, DAO, Manager, Executor. JDBC API. Database driver. Connection. Statement. ResultSet. Executing SQL statement. Модуль для работы с базой данных. Dependency Injection.
- Annotation. Object Relational Mapping. Hibernate.
Подробнее программу Технопарка вы можете посмотреть здесь.
Напомню, что полное название курса — «Углубленное программирование на Java». Поэтому право на резкий переход от первого занятия с Hello World к многопоточности на втором занятии у меня было. В аннотации к курсу мы написали, что для успешного восприятия студентам необходимо знакомство с базовыми понятиями в Java. Что из этого получится, можно было узнать только на практике.
Требования к слушателям курса
Из трех слов, составляющих название курса («Java», «программирование» и «углубленное»), слово «углубленное» — самое неоднозначное. Если бы курс назывался «Введение в Java», было бы понятно, о чем рассказывать. «Углубленное» означает, что слушатели уже знакомы с программированием — причем не просто на языке высокого уровня, а именно с программированием на Java. Мы решили, что студенты уже должны знать следующее:
- примитивные типы, переполнение
- объекты, классы, наследование, инкапсуляция, полиморфизм
- область видимости переменных, статические переменные
- ветвления и циклы
- преобразования примитивных типов
- интерфейсы, реализация интерфейса
- операторы, порядок выполнения операторов
- стек, куча
- модификаторы видимости (private, protected, public)
В Технопарке учатся студенты второго—пятого курсов различных факультетов МГТУ. К моему курсу они подошли после первого семестра обучения. Проверку на мотивацию они прошли; все, кто не обладал базовыми знаниями, базовые знания получили. Среди студентов были и те, кто уже пишет на Java и зарабатывает этим деньги, и те, кто до курса на Java вообще ничего не писал.
В результате часть слушателей на первой практической работе в первый раз написала «Hello world», а другая часть решила, что без шаблонизаторов и HTTP-сессий разрабатывать не интересно. По результатам первых двух занятий я решил, что правильно будет не ограничивать студентов, и разрешил использовать любые библиотеки, которые им известны. А к середине курса и вовсе разрешил писать свою Frontend-ную часть, то есть всю работу сервера с браузером организовать так, как им удобнее. При этом те, кто опыта работы на Java до этого не имел, могли продолжать учиться по моей программе. Использование дополнительных знаний на оценку не влияло.
Особенности чтения курса в Технопарке
Технопарк — дело добровольное. В том смысле, что мы не берем со студентов платы за обучение, не подписываем никаких контрактов и не обязываем посещать наши занятия — у студентов должна быть заинтересованность в посещении лекций и семинаров. Это должно быть их собственным желанием. А значит, сами занятия должны быть в первую очередь интересными. Если студент заинтересован происходящим, материал лекции он тоже запомнит и, скорее всего, придет на следующее занятие. Мы решили, что сделать не просто курс по Java, а курс по разработке игр — мало. Лекция в нашем понимании должна быть в некотором роде шоу. В первую очередь это отразилось в иллюстрациях к учебным материалам. Ниже — три примера из лекций:
Матрешки-Бэтмены – иллюстрация к теме «Шаблон Decorator в Java I/O»,
Обход дерева – иллюстрация к теме «Работа с XML-документами»
Вуки на слайде с большим количеством кода — чтобы глазу было на чем отдохнуть
Кроме слайдов, для привлечения внимания оказалось очень полезным задавать аудитории неожиданные вопросы. Например, «Кто помнит расстояние от Земли до Луны? Если у нас сервер с базами на Луне, какая будет минимальная задержка при записи в базу?». Или «Кто играл в корейские онлайновые игры? Вы можете объяснить термин ВКР?».
Позитивно на восприятии сказывается и упоминание названий и игровых терминов, которые студенты не ожидают услышать в университете.
Архитектура учебного сервера и практическая работа
Не все, что я написал о курсе до этого, может быть понятно с точки зрения преподавания теории по языку Java. Если ограничиться только прочтением лекций по приведенной выше программе, смысл многих разбираемых в курсе вопросов останется неясным. Например, зачем нам многопоточность на второй лекции, зачем нагрузочное тестирование на пятой, и почему аннотации только в конце курса? Эта странность будет понятна, если рассмотреть курс со стороны домашних заданий и практической работы.
Начну с описания задачи. Предлагаемый для разработки учебный сервер состоит из 8 модулей:
Подробно о каждом модуле:
- main — зависит от всех остальных модулей. Содержит функцию main(). Создает все остальные модули в момент старта сервера. Запускает frontend, dbService и gameMechanics в отдельных потоках. Инициализирует библиотеку Jetty, передает в Jetty ссылку на frontend.
- Frontend — это одновременно и handler для событий Jetty (через браузер пользователь передает запрос в Jetty, Jetty вызывает handle у Frontend), и служба, живущая в собственном потоке. Frontend хранит только те данные о пользователях, которые нужны для создания страницы и пересылки ее обратно в браузер. Все расчеты происходят в потоке gameMechanics. Задача фронтенда — принять запрос от пользователя, передать его в нужную службу и отдать пользователю страницу (в асинхронном режиме).
- dbService — сервис связи сервера с базой данных. Живет в отдельном потоке, держит коннект к базе. Обрабатывает запросы остальных модулей и возвращает им ответы. dbSerivice может быть несколько, каждый в своем потоке.
- gameMechanics — служба сервера, в которой происходят все игровые события. Игровой мир живет в этом потоке. Для всех остальных сервисов это просто черный ящик. Разные игры, написанные студентами, должны отличаться только игровой механикой. Игровая механика обсчитывает все события и отправляет реплику на Frontend.
- messageSystem — общий для всех потоков объект, через который происходит обмен данными между потоками. Содержит по одной очереди сообщений для каждого потока. Желающий обратиться к другому потоку должен положить в соответствующую очередь специальный объект – сообщение. Получатель достанет это сообщение из очереди и обработает его в удобное для себя время. Результатом обработки может стать отправка ответного сообщения.
- resourceSustem — singleton, который позволяет всем службам обратиться к файлам с параметрам работы сервера (ресурсам). Подробнее о ресурсах вы можете почитать здесь (http://dtf.ru/articles/read.php?id=60539).
- Утилиты – набор служб и хелперов для работы со временем, случайными числами и логами.
- base – набор интерфейсов и базовых классов всех служб.
Вы можете сравнить архитектуру учебного сервера с архитектурой сервера Аллодов. Заимствование не полное, но очевидно, что мы всячески старались им подражать.
Теперь о том, в какой последовательности мы все это писали. Работа была разбита на 6 семинаров:
- «Hello World», знакомство с Eclipse, знакомство с Jetty. Запуск web-сервера на localhost:8080, который возвращает страницу с «Hello Server!». Запоминание пользователя либо через сессии Jetty, либо через hidden поля на форме. Создание страницы, которая запрашивает саму себя раз в T миллисекунд.
- Запуск Frontend в отдельном потоке. Подсчёт из этого потока количества обращений, которые пришли к серверу со стороны пользователя. Запуск в отдельном потоке прототипа dbService (который пока к базе не обращается, а только имитирует обращение через кэш в памяти). Написание messageSystem для взаимодействия этих потоков через сообщения.
- Начало работы над игровой механикой. Студенты решают, какую игру они будут писать, и начинают разработку логики. Создание модуля gameMechanics в отдельном потоке, пересылка сообщений от Frontend к механике и обратно. Тестирование уже написанных модулей. Разработка unit-тестов и функциональных тестов.
- Создание утилит для работы со временем и случайными числами (если они нужны для механики). Создание модуля для работы с файловой системой. Перенос всех параметров из кода сервера в файлы (ресурсы).
- Работа с базами данных. Превращение «поддельного» dbService в настоящий.
- Завершение работы и защита сервера.
Семинары проходили очень живо. Я и мой ассистент бегали между студентами, отвечали на массу вопросов разной степени сложности. Студенты показывали друг другу свои решения. Мы разбирали общие для всех проблемы и трудности. Некоторые из вопросов были действительно интересными, и мое желание «узнать в процессе преподавания о себе и о Java что-то новое» было удовлетворено полностью.
Подход, при котором студент от состояния «ничего не писал на Java» за месяц переходит в состояние «написал web-сервер, который работает в 3 потока», может показаться жестким. Однако, как показывает практика, именно он работает лучше всего.
Результаты первого семестра
Большинство студентов предложенную выше программу освоили. Проблемы с восприятием материала были на третьей лекции, к концу которой мы разбирали взаимодействие потоков через систему обмена сообщениями. Сейчас я прочел эту же лекцию второму потоку студентов. И теперь мне кажется, что проблемы были не столько с восприятием, сколько с моими способностями объяснить материал. Повторное чтение курса, насколько я могу судить по степени остекленения глаз студентов, проходит гораздо продуктивнее.
Почти на всех лекциях я показывал свой код и показывал, как он работает. В результате, даже если студент что-то не понимал на лекции, он мог просмотреть ее в записи и скопировать сложные участки кода. Исходники своего кода я не раздавал, и, как следствие, даже при заимствовании кода из лекции этот код надо было, как минимум, переписать.
Главным результатом первого семестра я считаю доказанную студентами возможность написать за отведенное время проект web-севера по предложенной мной архитектуре. До защиты сервера дошли шесть групп из десяти. Три группы из шести подошли к задаче творчески и, кроме реализации всех модулей сервера, разработали интересную игровую механику.
Ну и, конечно, опрос студентов после завершения курса:
Экзамен и билеты
Экзамен сдавали только те студенты, которые не смогли вовремя выполнить практическую работу. Мы решили, что это теоретики, и что они, возможно, смогут добиться признания в роли архитекторов. Чтобы получить отличную оценку, надо было ответить на 3 вопроса, случайным образом взятых из двух одинаковых наборов по 49 вопросов в каждом. Вариант, при котором студент вытащит два одинаковых вопроса, мы не стали отбрасывать. В этом случае он должен был отвечать на один и тот же вопрос дважды. Правда, таких счастливчиков на экзамене не было.
Для полного представления о курсе привожу список вопросов:
- История и особенности языка Java.
- Платформа Java. Java Editions. GC, JVM, JDK, JRE. Bytecode.
- Правила наименований пакетов, классов, переменных и методов. Запуск Java-приложений
- class Object и class Class<?>. Основные методы этих классов.
- Простые типы в Java. Классы «обертки» простых типов.
- Generic programming в Java. Создание своих шаблонных классов и методов.
- Коллекции в Java. Iterator, Iterable.
- Processes и Threads.
- Interface Runnable. class Thread. Методы start() и run().
- Методы класса Thread: sleep(), interrupt(), join().
- Ключевые слова volatile и synchronized. Синхронизированные методы и выражения.
- Проблемы многопоточного доступа к данным. Race condition. Deadlock.
- Методы класса Object: wait(), notify() и notifyAll().
- Плюсы и минусы многопоточных приложений.
- Способы взаимодействия потоков.
- java.util.concurrent.
- MessageSystem. Address и Abonent.
- Распределение классов по пакетам. Циклические зависимости между пакетами.
- Архитектура игрового сервера. Схема зависимостей модулей.
- Процесс работы игрового сервера, репликация.
- Виды тестирования.
- GC. Виды GC. Параметры GC.
- Работа со случайными числами.
- Паттерны проектирования: Singleton и Context.
- Events. Подписка на события.
- Передача функции в библиотеку. Callback.
- Анонимные классы.
- Работа со временем и датой. Unix time. Locale.
- I/O Streams. Потоки байтов и потоки символов.
- Исключения: Throwable, Error, Exception, try, catch, finally.
- Сериализация/десериализация. Interface Serializable.
- Ключевое слово transient. Причины использования transient при сериализации.
- Reflection. Роль reflection в сериализации.
- Шаблон проектирования Factory method.
- SAX-парсер.
- DOM-парсер.
- Ресурсная система сервера.
- JDBC API.
- class Connection из JDBC API. Задачи, которые решает Connection.
- Dependency Injection. Использование при работе с базами данных.
- class Statement из JDBC API. Задачи, которые решает Statement.
- class ResultSet из JDBC API. Обработка ответа от базы.
- DataSet (элемент ORM).
- DAO (элемент ORM).
- Annotation. Использование аннотаций. Создание собственных аннотаций.
- Object Relational Mapping.
- Frontend — роль в составе сервера.
- Game Mechanics — роль в составе сервера.
- Database Service — роль в составе сервера.
Заключение
В заключение хочу заметить, что мы продолжаем работать над курсом. С каждым прочтением структура курса становится более логичной, а сам материал — более понятным. В планах на будущее – сделать курс еще более ориентированным на практику, включить в обязательную программу использование шаблонизаторов для создания страниц и практику по разворачиванию продукта на сервере. Но об этом я напишу позже, когда будет достаточно нового материала.
Благодарности
В работе над статьей и курсом мне помогали:
Александр Акбашев – QA сервера проекта SkyForge, аспирант МГТУ и мой ассистент в Технопарке.
Дмитрий Волошин – директор отдела исследований и образования Mail.Ru Group.
Сергей Загурский – руководитель команды сервера проекта SkyForge.
Тимур Бухараев – руководитель web-команды проекта SkyForge.
Автор: Tully