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

601 lines
30 KiB
TeX
Raw Permalink Normal View History

2024-05-12 20:23:51 +03:00
\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}