gb-java-devel/jtd2-07-workshop.tex

601 lines
30 KiB
TeX
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

\documentclass[j-spec.tex]{subfiles}
\usepackage{spreadtab}
\begin{document}
\section{Семинар: Интерфейсы и API}
\subsection{Инструментарий}
\begin{itemize}
\item \href{https://docs.google.com/presentation/d/1OBDGtC5dJz7Tu9zEJDayETOKZL0aPXne3Y8VUOGZUUU/edit?usp=share_link}{Презентация} для преподавателя, ведущего семинар;
\item \href{https://drive.google.com/file/d/1LWyE8aEy4-1gsognqhXIXwDcoLviVge4/view}{Фон} GeekBrains для проведения семинара в Zoom;
\item JDK любая 11 версии и выше;
\item \href{https://www.jetbrains.com/idea/download}{IntelliJ IDEA Community Edition} для практики и примеров используется IDEA.
\end{itemize}
\subsection{Цели семинара}
\begin{itemize}
\item Практика создания и проектирования интерфейсов;
\item передача и обработка сообщений с использованием интерфейсов;
\item Применение существующих интерфейсов для обработки исключений;
\item Отделение графического, сетевого и логического слоёв для приложения «Сетевой чат».
\end{itemize}
\subsection{План-содержание}
\noindent
\begin{spreadtab}{{longtable}{|p{37mm}|l|l|p{90mm}|}}
\hline
@ Что происходит & @ Время & @ Слайды & @ Описание \\
\hline
\endhead
@ Организационный момент & 5 tag(beg) & @ 1-5 & @ Преподаватель ожидает студентов, поддерживает активность и коммуникацию в чате, озвучивает цели и планы на семинар. Важно упомянуть, что выполнение домашних заданий с лекции является, фактически, подготовкой к семинару \\
\hline
@ Quiz & 5 & @ 6-18 & @ Преподаватель задаёт вопросы викторины, через 30 секунд демонстрирует слайд-подсказку и ожидает ответов (по минуте на ответ) \\
\hline
@ Рассмотрение ДЗ лекции & 10 & @ 19-24 & @ Преподаватель демонстрирует свой вариант решения домашнего задания с лекции, возможно, по предварительному опросу, демонстрирует и разбирает вариант решения одного из студентов \\
\hline
@ Вопросы и ответы & 10 & @ 25 & @ Преподаватель ожидает вопросов по теме прошедшей лекции, викторины и продемонстрированной работы \\
\hline
@ Задание 1 & 30 & @ 26-30 & @ Отделение бизнес-логики и графического интерфейса \\
\hline
@ Перерыв (если нужен) & 5 & @ 31 & @ Преподаватель предлагает студентам перерыв на 5 минут (студенты голосуют) \\
\hline
@ Задание 2 & 30 & @ 32-36 & @ Создание и частичная реализация интерфейсов для улучшения понимания механизмов взаимодействия объектов \\
\hline
@ Задание 3 & 10 & @ 37-40 & @ Написать абстрактный пример применения интерфейсов с реализацией по умолчанию и наследование \\
\hline
@ Домашнее задание & 5 & @ 41-42 & @ Объясните домашнее задание, подведите итоги урока \\
\hline
@ Рефлексия & 10 tag(end) & @ 43-44 & @ Преподаватель запрашивает обратную связь \\
\hline
@ Длительность & sum(cell(beg):cell(end)) & & \\
\hline
\end{spreadtab}
\subsection{Подробности}
\subsubsection*{Организационный момент}
\begin{itemize}
\item \textbf{Цель этапа:} Позитивно начать урок, создать комфортную среду для обучения.
\item \textbf{Тайминг:} 3-5 минут.
\item \textbf{Действия преподавателя:}
\begin{itemize}
\item Запрашивает активность от аудитории в чате;
\item Презентует цели курса и семинара;
\item Презентует краткий план семинара и что студент научится делать.
\end{itemize}
\end{itemize}
\subsubsection*{Quiz}
\begin{itemize}
\item \textbf{Цель этапа:} Вовлечение аудитории в обратную связь.
\item \textbf{Тайминг:} 5--7 минут (4 вопроса, по минуте на ответ).
\item \textbf{Действия преподавателя:}
\begin{itemize}
\item Преподаватель задаёт вопросы викторины, представленные на слайдах презентации;
\item через 30 секунд демонстрирует слайд-подсказку и ожидает ответов.
\end{itemize}
\item \textbf{Вопросы и ответы:}
\begin{enumerate}
\item Множественное наследование в Java (3)
\begin{enumerate}
\item запрещено;
\item разрешено;
\item разрешено для интерфейсов.
\end{enumerate}
\item Интерфейсы позволяют (2)
\begin{enumerate}
\item удобно создавать новые объекты, не связанные наследованием;
\item единообразно обращаться к методам объектов, не связанных наследованием;
\item полностью заменить наследование.
\end{enumerate}
\item Поле в интерфейсе (2)
\begin{enumerate}
\item невозможно;
\item public static final;
\item private final.
\end{enumerate}
\item Обработчик исключений для графического потока (2)
\begin{enumerate}
\item это статический метод;
\item это интерфейс;
\item реализован JVM.
\end{enumerate}
\end{enumerate}
\end{itemize}
\subsubsection*{Рассмотрение ДЗ}
\begin{itemize}
\item \textbf{Цель этапа:} Пояснить не очевидные моменты в формулировке ДЗ с лекции, синхронизировать прочитанный на лекции материал к началу семинара.
\item \textbf{Тайминг:} 15-20 минут.
\item \textbf{Действия преподавателя:}
\begin{itemize}
\item Преподаватель демонстрирует свой вариант решения домашнего задания из лекции;
\item возможно, по предварительному опросу, демонстрирует и разбирает вариант решения одного из студентов.
\end{itemize}
\item \textbf{Домашнее задание из лекции:}
\begin{itemize}
\item Полностью разобраться с кодом -- это задание не нужно обсуждать, возможно просто спросить, кто действительно сел, взял в руки карандаш и блокнот, и попытался разобраться с кодом с лекции. Похвалить тех, кто это действительно сделал.
\item Для приложения с шариками описать появление и убирание шариков по клику мышки левой и правой кнопкой соответственно
\textbf{Вариант решения}
На лекции был описан инициализатор приложения, заполняющий массив игровых объектов шариками. необходимо было описать слушатель мышки, добавляющий и убирающий шарики из массива
\begin{lstlisting}[language=Java,style=JCodeStyle]
if (e.getButton() == MouseEvent.BUTTON1) {
interactables[objectsCount++] = new Ball(e.getX(), e.getY());
} else if (e.getButton() == MouseEvent.BUTTON3) {
if (objectsCount == 1) return;
objectsCount--;
}
\end{lstlisting}
Для обеспечения работоспособности данного кода необходимо добавить в мячик конструктор, принимающий начальные координаты
\begin{lstlisting}[language=Java,style=JCodeStyle]
Ball(int x, int y) {
this();
this.x = x;
this.y = y;
}
\end{lstlisting}
\item Написать, выбросить и обработать такое исключение, которое не позволит создавать более, чем 15 шариков.
\textbf{Вариант решения}
Очевидно, что обработка исключений должна вызвать ряд проблем, поскольку тема обработки исключений при использовании графических интерфейсов рассматривается только на следующей лекции, но за попытку решить проблему следует похвалить студентов.
\begin{lstlisting}[language=Java,style=JCodeStyle,caption={Собственно, исключение}]
public class BallsOverflowException extends RuntimeException {
BallsOverflowException() {
super("Balls overflow, 15 allowed!");
}
}
\end{lstlisting}
По очевидным причинам выбрасывать исключение там, где добавляются шарики -- не логично, поэтому необходимо выбрасывать или на апдейте или на рендере.
\begin{lstlisting}[language=Java,style=JCodeStyle,caption={Место выброса исключения}]
private void update(MainCanvas canvas, float deltaTime) {
for (int i = 0; i < objectsCount; i++) {
interactables[i].update(canvas, deltaTime);
}
if (objectsCount >= 15)
throw new BallsOverflowException();
}
\end{lstlisting}
Для обработки исключений в Swing необходимо установить обработчик исключений для потока. сделаем этим обработчиком «себя».
\begin{lstlisting}[language=Java,style=JCodeStyle,caption={Обработка исключения}]
private MainWindow() { //constructor
Thread.setDefaultUncaughtExceptionHandler(this);
}
@Override
public void uncaughtException(Thread t, Throwable e) {
if (e.getClass().equals(BallsOverflowException.class)) {
System.out.println(e.getMessage());
}
}
\end{lstlisting}
\item ** Написать ещё одно приложение, в котором на белом фоне будут перемещаться изображения формата png, лежащие в папке проекта.
\textbf{Вариант решения}
Главным отличием приложения будет работа с изображением. Такова была идея примера -- тиражируемость приложения. То есть вообще весь код может быть взят из шариков или квадратиков, но в главном рисуемом объекте должен происходить не рендеринг примитива, а рендеринг картинки.
\begin{lstlisting}[language=Java,style=JCodeStyle,caption={}]
public class Ball extends Sprite {
private Image img = null;
private float vX;
private float vY;
Ball() {
halfHeight = 64; // logo size magic numbers
halfWidth = 62;
try {
img = ImageIO.read(new File("logo.png"));
} catch (IOException e) {
throw new RuntimeException(e);
}
vX = 100f + (float) (Math.random() * 200f);
vY = 100f + (float) (Math.random() * 200f);
}
@Override
public void render(MainCanvas canvas, Graphics g) {
g.drawImage(img, (int) getLeft(), (int) getTop(), null);
}
}
\end{lstlisting}
\end{itemize}
\end{itemize}
\subsubsection*{Вопросы и ответы}
\begin{itemize}
\item \textbf{Ценность этапа} Вовлечение аудитории в обратную связь, пояснение неочевидных моментов в материале лекции и другой проделанной работе.
\item \textbf{Тайминг} 5-15 минут
\item \textbf{Действия преподавателя}
\begin{itemize}
\item Преподаватель ожидает вопросов по теме прошедшей лекции, викторины и продемонстрированной работы;
\item Если преподаватель затрудняется с ответом, необходимо мягко предложить студенту ответить на его вопрос на следующем семинаре (и не забыть найти ответ на вопрос студента!);
\item Предложить и показать пути самостоятельного поиска студентом ответа на заданный вопрос;
\item Посоветовать литературу на тему заданного вопроса;
\item Дополнительно указать на то, что все сведения для выполнения домашнего задания, прохождения викторины и работы на семинаре были рассмотрены в методическом материале к этому или предыдущим урокам.
\end{itemize}
\end{itemize}
\subsubsection*{Задание 1}
\begin{itemize}
\item \textbf{Ценность этапа} Отделение бизнес-логики и графического интерфейса.
\item \textbf{Тайминг} 25-30 мин
\item \textbf{Действия преподавателя}
\begin{itemize}
\item Выдать задание студентам;
\item Подробно объяснить, что именно требуется от студентов, избегая упоминания конкретных языковых конструкций;
\item Пояснить студентам пользу и необходимость следования паттерну MVC, проговорить важность разделения логики и открывающиеся возможности при правильном проектировании.
\end{itemize}
\item \textbf{Задание}:
\begin{itemize}
\item На предыдущем семинаре было описано окно сервера приложения, содержащее две кнопки (старт и стоп) и текстовое поле журнала. Необходимо вынести логику работы сервера в класс \code{ChatServer}, а в обработчиках кнопок оставить только логику нажатия кнопки и журналирования сообщений от сервера.
Для достижения цели необходимо описать интерфейс «слушатель сервера», с методом «получить сообщение», вызывать его с одной стороны, и реализовать с другой.
\textbf{Вариант решения}
\begin{lstlisting}[language=Java,style=JCodeStyle]
//ChatServerListener.java
package ru.gb.jdk.two.sem;
public interface ChatServerListener {
void onMessageReceived(String msg);
}
//ChatServer.java
package ru.gb.jdk.two.sem;
public class ChatServer {
private boolean isServerWorking;
private final ChatServerListener listener;
ChatServer(ChatServerListener listener) {
isServerWorking = false;
this.listener = listener;
}
public void start() {
if (isServerWorking) {
listener.onMessageReceived("Server is already working");
return;
}
listener.onMessageReceived("Server started");
isServerWorking = true;
}
public void stop() {
if (!isServerWorking) {
listener.onMessageReceived("Server is stopped");
return;
}
listener.onMessageReceived("Server stopped");
isServerWorking = false;
}
}
//ServerWindow.java
//constructor
server = new ChatServer(this);
btnStop.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
server.stop();
}
});
btnStart.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
server.start();
}
});
// method implementation
@Override
public void onMessageReceived(String msg) {
log.append(msg + "\n");
}
\end{lstlisting}
\item [$*_1$] Отделить функционал логгирования сообщений сервера для простоты изменения способа оповещения пользователя.
\textbf{Вариант решения}
\begin{lstlisting}[language=Java,style=JCodeStyle]
@Override
public void onMessageReceived(String msg) {
putMessageToLog(msg);
}
private void putMessageToLog(String msg) {
log.append(msg + "\n");
}
\end{lstlisting}
\end{itemize}
\end{itemize}
\subsubsection*{Задание 2}
\begin{itemize}
\item \textbf{Ценность этапа} Создание и частичная реализация интерфейсов для улучшения понимания механизмов взаимодействия объектов.
\item \textbf{Тайминг} 25-30 мин
\item \textbf{Действия преподавателя}
\begin{itemize}
\item Выдать задание студентам;
\item Подробно объяснить, что именно требуется от студентов, избегая упоминания конкретных языковых конструкций;
\item Если группа студентов справилась с заданием, а времени осталось более 5 минут, выдавать группе задания «со звёздочкой».
\end{itemize}
\item \textbf{Задание}:
\begin{itemize}
\item Создать интерфейсы \code{ServerSocketThreadListener} и \code{SocketThreadListener}, содержащие методы, соответствующие событиям сервера и клиента чата. Реализовать созданные интерфейсы простым логированием. Со стороны клиента -- только \code{SocketThreadListener}, со стороны сервера -- оба интерфейса.
\textbf{Вариант решения}
\begin{lstlisting}[language=Java,style=JCodeStyle]
package ru.gb.jdk.two.sem.network;
import java.net.Socket;
public interface SocketThreadListener {
void onSocketStart(Socket s);
void onSocketStop();
void onSocketReady(Socket socket);
void onReceiveString(Socket s, String msg);
void onSocketException(Throwable e);
}
package ru.gb.jdk.two.sem.network;
import java.net.ServerSocket;
import java.net.Socket;
public interface ServerSocketThreadListener {
void onServerStart();
void onServerStop();
void onServerSocketCreated(ServerSocket s);
void onServerSoTimeout(ServerSocket s);
void onSocketAccepted(ServerSocket s, Socket client);
void onServerException(Throwable e);
}
\end{lstlisting}
\begin{lstlisting}[language=Java,style=JCodeStyle]
// Server
/**
* Server socket thread methods
* */
@Override
public void onServerStart() { listener.onMessageReceived("Server thread started"); }
@Override
public void onServerStop() { listener.onMessageReceived("Server thread stopped"); }
@Override
public void onServerSocketCreated(ServerSocket s) { listener.onMessageReceived("Server socket created"); }
@Override
public void onServerSoTimeout(ServerSocket s) {
// listener.onMessageReceived("Accept timeout");
}
@Override
public void onSocketAccepted(ServerSocket s, Socket client) { listener.onMessageReceived("client connected"); }
@Override
public void onServerException(Throwable e) { e.printStackTrace(); }
/**
* Socket Thread listening
* */
@Override
public synchronized void onSocketStart(Socket s) { listener.onMessageReceived("Client connected"); }
@Override
public synchronized void onSocketStop() { listener.onMessageReceived("Client dropped"); }
@Override
public synchronized void onSocketReady(Socket socket) { listener.onMessageReceived("Client is ready"); }
@Override
public synchronized void onReceiveString(Socket s, String msg) { listener.onMessageReceived(msg); }
@Override
public void onSocketException(Throwable e) { e.printStackTrace(); }
// Client
@Override
public void onSocketStart(Socket s) { log.append("Started" + "\n"); }
@Override
public void onSocketStop() { log.append("Stopped" + "\n"); }
@Override
public void onSocketReady(Socket socket) { log.append("Ready" + "\n"); }
@Override
public void onReceiveString(Socket s, String msg) { log.append(msg + "\n"); }
@Override
public void onSocketException(Throwable e) { e.printStackTrace(); }
\end{lstlisting}
\item [$*_1$] Создать классы -- \code{ServerSocketThread} и \code{SocketThread}, соответственно слушателям, то есть реализовать в классе конструкторы с возможностью сохранения ссылки на слушателей. Создать экземпляр \code{ServerSocketThread} из объекта \code{ChatServer}, а \code{SocketThread} из \code{ClientGUI}. На данном этапе, не важно, где именно будут создаваться эти объекты.
\textbf{Вариант решения}
\begin{lstlisting}[language=Java,style=JCodeStyle]
package ru.gb.jdk.two.sem.network;
public class ServerSocketThread {
private ServerSocketThreadListener listener;
public ServerSocketThread(ServerSocketThreadListener listener) {
this.listener = listener;
}
}
package ru.gb.jdk.two.sem.network;
public class SocketThread {
private final SocketThreadListener listener;
public SocketThread(SocketThreadListener listener) {
this.listener = listener;
}
}
// Server
private ServerSocketThread server;
public void start() {
if (isServerWorking) {
listener.onMessageReceived("Server is already working");
return;
}
server = new ServerSocketThread(this);
listener.onMessageReceived("Server started");
isServerWorking = true;
}
//Client
private void connect() {
SocketThread socketThread = new SocketThread(this);
}
\end{lstlisting}
\end{itemize}
\end{itemize}
\subsubsection*{Задание 3}
\begin{itemize}
\item \textbf{Ценность этапа} Написать абстрактный пример применения интерфейсов с реализацией по умолчанию и наследованием.
\item \textbf{Тайминг} 10-15 мин
\item \textbf{Действия преподавателя}
\begin{itemize}
\item Выдать задание студентам;
\item Подробно объяснить, что именно требуется от студентов, избегая упоминания конкретных языковых конструкций;
\end{itemize}
\item \textbf{Задание}:
\begin{itemize}
\item Описать команду разработчиков. В команде разработчиков могут находиться бэкендеры, которые в состоянии писать серверный код, фронтендеры, которые могут программировать экранные формы, и фуллстэк разработчики, совмещающие в себе обе компетенции. Реализовать класс фулстэк разработчика, создать экземпляр и последовательно вызвать все его методы.
\textbf{Вариант решения}
\begin{lstlisting}[language=Java,style=JCodeStyle]
package ru.gb.jdk.two.sem.devs;
public interface Backender { void developServer(); }
public interface Frontender { void developGUI(); }
public interface Fullstack extends Backender, Frontender {
}
public class FullstackDeveloper implements Fullstack {
@Override
public void developServer() { System.out.println("Server done"); }
@Override
public void developGUI() { System.out.println("GUI done"); }
}
public class Main {
public static void main(String[] args) {
FullstackDeveloper dev = new FullstackDeveloper();
dev.developGUI();
dev.developServer();
}
}
\end{lstlisting}
\item [$*_1$] Добавить возможность при создании экземпляров классов любых интерфейсов -- добавлять их в один массив, например, \code{Developer[] team = {...};}
\textbf{Вариант решения}
К первому решению добавляется маркерный интерфейс «Разработчик». Очевидно, что нельзя для этого интерфейса вызвать более точные методы фуллстэк разработчика.
\begin{lstlisting}[language=Java,style=JCodeStyle]
package ru.gb.jdk.two.sem.devs;
public interface Developer {
}
public interface Backender extends Developer { void developServer(); }
public interface Frontender extends Developer { void developGUI(); }
public class Main {
public static void main(String[] args) {
Developer dev = new FullstackDeveloper();
}
}
\end{lstlisting}
\end{itemize}
\end{itemize}
\subsubsection*{Домашнее задание}
\begin{itemize}
\item \textbf{Ценность этапа} Задать задание для самостоятельного выполнения между занятиями. В данном семинаре домашнее задание не предусмотрено в связи со сложностью заданий семинара. Необходимо досконально разобраться в написанном (и запланированном коде) и взаимосвязях объектов. Также возможно уточнить, что задания семинара направлены на написание многопоточного сетевого чата, но, поскольку это будет достаточно сложный проект, его написание приводится постепенно
\item \textbf{Тайминг} 5-10 минут.
\item \textbf{Действия преподавателя}
\begin{itemize}
\item Пояснить студентам в каком виде выполнять и сдавать задания
\item Уточнить кто будет проверять работы (преподаватель или ревьювер)
\item Объяснить к кому обращаться за помощью и где искать подсказки
\item Объяснить где взять проект заготовки для дз
\end{itemize}
\item \textbf{Задания}
\begin{enumerate}
\item [5-25 мин] Выполнить все задания семинара, если они не были решены, без ограничений по времени;
\textbf{Все варианты решения приведены в тексте семинара выше}
\item [5 мин] 1. Дописать третье задание таким образом, чтобы в идентификатор типа \code{Developer} записывался объект \code{Frontender}, и далее вызывался метод \code{developGUI()}, не изменяя существующие интерфейсы, только код основного класса.
\begin{lstlisting}[language=Java,style=JCodeStyle]
public class Main {
public static void main(String[] args) {
Developer dev = new FrontendDeveloper();
if (dev instanceof Frontender) {
((Frontender) dev).developGUI();
}
}
}
\end{lstlisting}
\end{enumerate}
\end{itemize}
\subsubsection*{Рефлексия и завершение семинара}
\begin{itemize}
\item \textbf{Цель этапа:} Привести урок к логическому завершению, посмотреть что студентам удалось, что было сложно и над чем нужно еще поработать
\item \textbf{Тайминг:} 5-10 минут
\item \textbf{Действия преподавателя:}
\begin{itemize}
\item Запросить обратную связь от студентов.
\item Подчеркните то, чему студенты научились на занятии.
\item Дайте рекомендации по решению заданий, если в этом есть необходимость
\item Дайте краткую обратную связь студентам.
\item Поделитесь ощущением от семинара.
\item Поблагодарите за проделанную работу.
\end{itemize}
\end{itemize}
\newpage
\end{document}