gb-java-devel/jtd3-08-workshop.tex

625 lines
35 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{Семинар: Обобщения}
\subsection{Инструментарий}
\begin{itemize}
\item \href{https://docs.google.com/presentation/d/1rWHGQBITRElvyKFEjwe8dOinDbAV2ZW18tAK1FXU48U/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-23 & @ Преподаватель демонстрирует свой вариант решения домашнего задания с лекции, возможно, по предварительному опросу, демонстрирует и разбирает вариант решения одного из студентов \\
\hline
@ Вопросы и ответы & 10 & @ 24 & @ Преподаватель ожидает вопросов по теме прошедшей лекции, викторины и продемонстрированной работы \\
\hline
@ Задание 1 & 10 & @ 25-27 & @ Формирование навыков базовой работы с обобщениями \\
\hline
@ Задание 2 & 20 & @ 28-30 & @ Практика написания обобщённого кода с дополнительным описанием алгоритмов манипулирования данными \\
\hline
@ Перерыв (если нужен) & 5 & @ 31 & @ Преподаватель предлагает студентам перерыв на 5 минут (студенты голосуют) \\
\hline
@ Задание 3 & 20 & @ 32-34 & @ Изучение поведения итератора для понимания механизмов его действия и необходимости его использования в работе \\
\hline
@ Задание 4 & 20 & @ 35-38 & @ Закрепление понимания механизмов работы коллекций (в том числе с применением итераторов) \\
\hline
@ Домашнее задание & 5 & @ 39-40 & @ Объясните домашнее задание, подведите итоги урока \\
\hline
@ Рефлексия & 10 tag(end) & @ 41-42 & @ Преподаватель запрашивает обратную связь \\
\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 generics? (1)
\begin{enumerate}
\item Упрощение программирования и повышение безопасности типов
\item Ускорение работы программы и сокращение объема кода
\item Позволяют работать с различными базами данных одновременно
\end{enumerate}
% Неверные ответы: b) и c) - generics не связаны с ускорением работы программы или работой с базами данных, их основная цель - добавить типовую безопасность и упростить программирование.
\item Что такое параметризованный класс в Java generics? (1)
\begin{enumerate}
\item Класс, который принимает параметр типа
\item Класс, который имеет только один параметр типа
\item Класс, который можно использовать только с определенным типом данных
\end{enumerate}
% Неверные ответы: b) и c) - параметризованный класс может иметь любое количество параметров типа и может быть использован с различными типами данных.
\item Какие ограничения можно использовать с wildcard в Java generics? (1)
\begin{enumerate}
\item extends и super
\item only
\item extends
\end{enumerate}
% Неверные ответы: b) и c) - в generics для ограничения типа используются ключевые слова extends и super, а не only или extends.
\item Можно ли создать экземпляр обобщенного типа в Java, внутри класса, описывающего этот тип? (2)
\begin{enumerate}
\item Да, это возможно
\item Нет, такое создание экземпляров обобщенных типов запрещено
\item Это зависит от контекста использования обобщенного типа
\end{enumerate}
% Неверные ответы: a) и c) - экземпляры обобщенных типов не могут быть созданы в Java из-за стирания типов, однако можно создать экземпляр обобщенного класса, указав конкретный тип данных.
\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 Написать метод, который меняет два элемента массива местами (массив может быть любого ссылочного типа);
\textbf{Вариант решения}
Задание с подвохом, при решении необязательно было использовать дженерики, формулировка задания не ограничивает используемые в массиве типы, наоборот, явно проговаривается, что массив может быть любого типа.
\begin{lstlisting}[language=Java,style=JCodeStyle]
private static void swap(Object[] arr, int from, int to) {
Object temp = arr[from];
arr[from] = arr[to];
arr[to] = temp;
}
public static void main(String[] args) {
Object[] arr = {1, 2.0f, "hello"};
System.out.println(Arrays.toString(arr));
swap(arr, 0, 2);
System.out.println(Arrays.toString(arr));
}
\end{lstlisting}
\item Большая задача:
\begin{itemize}
\item Есть классы \code{Fruit} -> \code{Apple}, \code{Orange}; (больше не надо);
\item Класс \code{Box} в который можно складывать фрукты, коробки условно сортируются по типу фрукта, поэтому в одну коробку нельзя сложить и яблоки, и апельсины; Для хранения фруктов внутри коробки можете использовать \code{ArrayList};
\item Сделать метод \code{getWeight()} который высчитывает вес коробки, зная количество фруктов и вес одного фрукта(вес яблока -- \code{1.0f}, апельсина -- \code{1.5f}, не важно в каких единицах);
\item Внутри класса коробки сделать метод \code{compare()}, который позволяет сравнить текущую коробку с той, которую подадут в \code{compare()} в качестве параметра, \code{true} -- если их веса равны, \code{false} в противном случае (коробки с яблоками возможно сравнивать с коробками с апельсинами);
\item Написать метод, который позволяет пересыпать фрукты из текущей коробки в другую коробку (при этом, нельзя яблоки высыпать в коробку с апельсинами), соответственно, в текущей коробке фруктов не остается, а в другую перекидываются объекты, которые были в этой коробке.
\end{itemize}
\textbf{Вариант решения}
Создали фрукты с каким то разным весом, создали коробку, которая может вместить только фрукты. Надо фрукты там как то хранить, поэтому -- список. Соответственно при создании можем в нашу коробку какие то фрукты накидать. Соответственно метод, который будет уметь добавлять фрукты в коробку
\begin{lstlisting}[language=Java,style=JCodeStyle,caption={ }]
private interface Fruit { float getWeight(); }
private static class Apple implements Fruit {
static final float weight = 1.0f;
@Override public float getWeight() { return weight; }
}
private static class Orange implements Fruit {
static final float weight = 1.5f;
@Override public float getWeight() { return weight; }
}
public static class Box<T extends Fruit> {
private final List<T> container;
Box(T[] init) {
container = new ArrayList<>();
for (T f : init) { add(f); }
}
void add(T fruit) { container.add(fruit); }
void print() { System.out.println(getWeight()); }
}
\end{lstlisting}
Поскольку в коробке у нас могут быть только одно типа фрукты -- мы можем не перебирать каждый из них, а умножить вес одного на количество.
\begin{lstlisting}[language=Java,style=JCodeStyle,caption={ }]
float getWeight() {
return (container.isEmpty())
? 0
: container.get(0).getWeight() * container.size();
}
\end{lstlisting}
Можно было, по большому счёту, переопределить метод equals() или написать свой метод compare() который принимает вторую коробку и сравнивает вес. Сравнивать можно любые фрукты, значит в методе не подходит параметр типа \code{<T>}
\begin{lstlisting}[language=Java,style=JCodeStyle,caption={ }]
boolean compare(Box<?> with) {
return this.getWeight() - with.getWeight() < 0.0001;
}
\end{lstlisting}
Осталось только уметь пересыпать из одной коробки в другую не смешивая. На входе должна быть коробка того же типа, что и у объекта, и тут два пути, либо пересыпать всё из текущей коробки в другую, либо из другой в текущую, метод назван \code{transferTo()}, поэтому пересыпаться фрукты будут из текущей коробки. Возникает вопрос: если создаётся коробка с яблоками \code{Box<Apple>} и коробка с фруктами \code{Box<Fruit>}, должна ли быть возможность перекинуть из яблочной во фруктовую? Однозначно, нет. Поэтому, в аргументе метода используется \code{Box<? super T>}, таким образом яблоки возможно кидать в яблоки или в яблочных родителей, но не в апельсины.
\begin{lstlisting}[language=Java,style=JCodeStyle,caption={ }]
void transferTo(Box<? super T> dest) {
dest.container.addAll(container);
container.clear();
}
\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{Тайминг} 10-15 мин
\item \textbf{Действия преподавателя}
\begin{itemize}
\item Выдать задание студентам;
\item Подробно объяснить, что именно требуется от студентов, избегая упоминания конкретных языковых конструкций;
\item Пояснить студентам пользу и необходимость следования паттерну MVC, проговорить важность разделения логики и открывающиеся возможности при правильном проектировании.
\end{itemize}
\item \textbf{Задание}:
\begin{itemize}
\item Создать обобщенный класс с тремя параметрами (\code{T}, \code{V}, \code{K}). Класс содержит три переменные типа (\code{T}, \code{V}, \code{K}), конструктор, принимающий на вход параметры типа (\code{T}, \code{V}, \code{K}), методы возвращающие значения трех переменных. Создать метод, выводящий на консоль имена классов для трех переменных класса. Наложить ограничения на параметры типа: \code{T} должен реализовать интерфейс \code{Comparable} (классы оболочки, \code{String}), \code{V} должен реализовать интерфейс \code{DataInput} и расширять класс \code{InputStream}, \code{K} должен расширять класс \code{Number}.
\textbf{Вариант решения}
\begin{lstlisting}[language=Java,style=JCodeStyle]
private static class MyClass<T extends Comparable, V extends InputStream & Serializable, K extends Number> {
T t; V v; K k;
MyClass(T t, V v, K k) {
this.t = t; this.v = v; this.k = k;
}
public T getT() { return t; }
public V getV() { return v; }
public K getK() { return k; }
void print() {
System.out.printf("t = %s, v = %s, k = %s",
t.getClass().getName(),
v.getClass().getName(),
k.getClass().getName());
}
}
\end{lstlisting}
\end{itemize}
\end{itemize}
\subsubsection*{Задание 2}
\begin{itemize}
\item \textbf{Ценность этапа} Практика написания обобщённого кода с дополнительным описанием алгоритмов манипулирования данными.
\item \textbf{Тайминг} 15-20 мин
\item \textbf{Действия преподавателя}
\begin{itemize}
\item Выдать задание студентам;
\item Подробно объяснить, что именно требуется от студентов, избегая упоминания конкретных языковых конструкций;
\item Если группа студентов справилась с заданием, а времени осталось более 5 минут, выдавать группе задания «со звёздочкой».
\end{itemize}
\item \textbf{Задание}:
\begin{itemize}
\item Описать собственную коллекцию -- список на основе массива. Коллекция должна иметь возможность хранить любые типы данных, иметь методы добавления и удаления элементов.
\textbf{Вариант решения}
\begin{lstlisting}[language=Java,style=JCodeStyle]
private static class OwnList<T> {
Object[] arr;
int count;
OwnList() {
arr = new Object[1];
count = 0;
}
void add(T item) {
if (count == arr.length) {
Object[] newArr = new Object[arr.length * 2];
System.arraycopy(arr, 0, newArr, 0, arr.length);
arr = newArr;
}
arr[count++] = item;
}
T remove() {
if (count == 0) throw new NoSuchElementException();
T temp = (T) arr[--count];
arr[count] = null;
return temp;
}
@Override
public String toString() {
return Arrays.toString(arr);
}
}
\end{lstlisting}
\end{itemize}
\end{itemize}
\subsubsection*{Задание 3}
\begin{itemize}
\item \textbf{Ценность этапа} Изучение поведения итератора для понимания механизмов его действия и необходимости его использования в работе.
\item \textbf{Тайминг} 20-25 мин
\item \textbf{Действия преподавателя}
\begin{itemize}
\item Выдать задание студентам;
\item Подробно объяснить, что именно требуется от студентов, избегая упоминания конкретных языковых конструкций;
\end{itemize}
\item \textbf{Задание}:
\begin{itemize}
\item Написать итератор по массиву. Итератор -- это объект, осуществляющий движение по коллекциям любого типа, содержащим любые типы данных. Итераторы обычно имеют только два метода -- проверка на наличие следующего элемента и переход к следующему элементу. Но также, особенно в других языках программирования, возможно встретить итераторы, реализующие дополнительную логику.
\textbf{Вариант решения}
\begin{lstlisting}[language=Java,style=JCodeStyle]
private static class ArrayIterator<T> {
private final T[] array;
private int index = 0;
public ArrayIterator(T[] array) {
this.array = array;
}
public boolean hasNext() {
return index < array.length;
}
public T next() {
if(!hasNext())
throw new NoSuchElementException();
return array[index++];
}
}
public static void main(String[] args) {
Integer[] arr = {1,2,3,4};
ArrayIterator<Integer> it = new ArrayIterator<>(arr);
while (it.hasNext()) {
System.out.print(it.next() + " ");
}
}
\end{lstlisting}
\item [$*_1$] Написать итератор таким образом, чтобы его возможно было использовать для цикла \code{foreach}.
\textbf{Вариант решения}
Указать, что для этого достаточно реализовать интерфейс \code{Iterator<T>}.
\begin{lstlisting}[language=Java,style=JCodeStyle]
class ArrayIterator<T> implements Iterator<T>{
private T[] array;
private int index = 0;
public ArrayIterator(T[] array) {
this.array = array;
}
@Override
public boolean hasNext() {
return index < array.length;
}
@Override
public T next() {
if(!hasNext())
throw new NoSuchElementException();
return array[index++];
}
}
\end{lstlisting}
\end{itemize}
\end{itemize}
\subsubsection*{Задание 4}
\begin{itemize}
\item \textbf{Ценность этапа} Закрепление понимания механизмов работы коллекций (в том числе с применением итераторов).
\item \textbf{Тайминг} 20-25 мин
\item \textbf{Действия преподавателя}
\begin{itemize}
\item Выдать задание студентам;
\item Подробно объяснить, что именно требуется от студентов, избегая упоминания конкретных языковых конструкций;
\end{itemize}
\item \textbf{Задание}:
\begin{itemize}
\item Описать интерфейс \code{Person} с методами \code{doWork()} и \code{haveRest()}. Написать два класса работник и бездельник, реализующих интерфейс. Работник работает, и не умеет бездельничать, в то время как бездельник не умеет работать, но умеет отдыхать. Написать обобщённые классы \code{Workplace} и \code{Club}, содержащие массив из \code{Person}. В классах необходимо вызывать у всего массива людей вызывать соответствующие методы.
\textbf{Вариант решения}
\begin{lstlisting}[language=Java,style=JCodeStyle]
interface Person {
void doWork();
void haveRest();
}
private static class Worker implements Person {
@Override public void doWork() {
System.out.println("Work!"); }
@Override public void haveRest() {
System.out.println("What?");
}
}
private static class Idler implements Person {
@Override public void doWork() {
System.out.println("No!"); }
@Override public void haveRest() {
System.out.println("Chill!");
}
}
private static class Workplace<T extends Person> {
Person[] arr;
public Workplace(T... people) {
arr = people;
}
void work() {
for (Person person : arr) { person.doWork(); }
}
}
private static class Club<T extends Person> {
Person[] arr;
public Club(T... people) {
arr = people;
}
void chill() {
for (Person person : arr) { person.haveRest(); }
}
}
public static void main(String[] args) {
Workplace<Person> w = new Workplace<>(new Worker(), new Worker(), new Idler());
Club<Person> c = new Club<>(new Worker(), new Worker(), new Idler());
w.work();
c.chill();
}
\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 [25 мин] 1. Внедрить итератор из задания 2 в коллекцию, написанную в задании 3 таким образом, чтобы итератор был внутренним классом и, соответственно, объектом в коллекции.
\begin{lstlisting}[language=Java,style=JCodeStyle]
private static class OList<T> implements Iterable<T> {
class ArrayIterator<T> implements Iterator<T> {
private int index = 0;
@Override
public boolean hasNext() {
return index < count;
}
@Override
public T next() {
if(!hasNext())
throw new NoSuchElementException();
return (T) arr[index++];
}
}
Object[] arr;
int count;
Main.OList.ArrayIterator iter;
OList() {
arr = new Object[1];
count = 0;
this.iter = new ArrayIterator<T>();
}
void add(T item) {
if (count == arr.length) {
Object[] newArr = new Object[arr.length * 2];
System.arraycopy(arr, 0, newArr, 0, arr.length);
arr = newArr;
}
arr[count++] = item;
}
T remove() {
if (count == 0) throw new NoSuchElementException();
--count;
T temp = (T) arr[count];
arr[count] = null;
return temp;
}
@Override
public Iterator<T> iterator() {
return iter;
}
@Override
public Spliterator<T> spliterator() {
return Iterable.super.spliterator();
}
@Override
public String toString() {
return Arrays.toString(arr);
}
}
\end{lstlisting}
\item [15 мин] 2. Написать класс Калькулятор (необобщенный), который содержит обобщенные статические методы: \code{sum()}, \code{multiply()}, \code{divide()}, \code{subtract()}. Параметры этих методов -- два \textbf{числа} разного типа, над которыми должна быть произведена операция.
\begin{lstlisting}[language=Java,style=JCodeStyle]
private static class Calculator {
public static <T extends Number, U extends Number> double sum(T num1, U num2) {
return num1.doubleValue() + num2.doubleValue();
}
public static <T extends Number, U extends Number> double multiply(T num1, U num2) {
return num1.doubleValue() * num2.doubleValue();
}
public static <T extends Number, U extends Number> double divide(T num1, U num2) {
return num1.doubleValue() / num2.doubleValue();
}
public static <T extends Number, U extends Number> double subtract(T num1, U num2) {
return num1.doubleValue() - num2.doubleValue();
}
}
public static void main(String[] args) {
System.out.println(Calculator.sum(2, 2.0));
System.out.println(Calculator.multiply(2.0f, 2.0));
System.out.println(Calculator.divide(2L, 2.0));
System.out.println(Calculator.subtract(2, 2));
}
\end{lstlisting}
\item [10 мин] 3. Напишите обобщенный метод \code{compareArrays()}, который принимает два массива и возвращает \code{true}, если они одинаковые, и \code{false} в противном случае. Массивы могут быть любого типа данных, но должны иметь одинаковую длину и содержать элементы одного типа.
\begin{lstlisting}[language=Java,style=JCodeStyle]
private static <T> boolean compareArrays(T[] array1, T[] array2) {
if (array1.length != array2.length) {
return false;
}
for (int i = 0; i < array1.length; i++) {
if (!array1[i].equals(array2[i])) {
return false;
}
}
return true;
}
\end{lstlisting}
\item [10 мин] 4. Напишите обобщенный класс \code{Pair}, который представляет собой пару значений разного типа. Класс должен иметь методы \code{getFirst()}, \code{getSecond()} для получения значений пары, а также переопределение метода \code{toString()}, возвращающее строковое представление пары.
\begin{lstlisting}[language=Java,style=JCodeStyle]
private static class Pair<T1, T2> {
private T1 first;
private T2 second;
public Pair(T1 first, T2 second) {
this.first = first;
this.second = second;
}
public T1 getFirst() { return first; }
public T2 getSecond() { return second; }
@Override public String toString() {
return "(" + first + ", " + second + ")";
}
}
public static void main(String[] args) {
Pair<Integer, String> pair1 = new Pair<>(1, "one");
System.out.println(pair1.getFirst()); // 1
System.out.println(pair1.getSecond()); // "one"
System.out.println(pair1); // "(1, one)"
Pair<String, Double> pair2 = new Pair<>("pi", 3.14);
System.out.println(pair2.getFirst()); // "pi"
System.out.println(pair2.getSecond()); // 3.14
System.out.println(pair2); // "(pi, 3.14)"
}
\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}