\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}