Задачи для практических занятий по учебному курсу “ООП” (3 курс)
Это важно знать:
Преподаватель практики может предложить собственную линейку задач для проведения практических занятий Задачи разделены на тематические блоки. В пределах блока студент имеет возможность выбрать одну из предложенных задач. В зачет идет лишь какая-то одна из задач блока.
Первое знакомство с Java
Цели: научиться создавать простейшие программы на Java, компилировать их и запускать из командной строки познакомиться со средой Eclipse, научиться использовать ее для создания, компиляции и запуска программ освоить базовые управляющие инструкции языка (вместо HelloWorld следует использовать более сложный пример программы, рассматриваемый на лекции).
1a. Hello World
Взять исходник программы HelloWorld из материалов лекций, научиться компилировать и запускать эту программу из командной строки и из среды Eclipse
Java и процедурный стиль
Цели:
научиться программировать на Java в процедурном стиле. попробовать трансформировать программу с процедурного стиля в стиль ООП начать отрабатывать Правила хорошего кода.
При использовании приемов ООП следует добиться отделения логики ввода/вывода от логики обработки строки.
2a. Статистика символов.
Написать программу в процедурном стиле, которая вводит с консоли строку русского текста, после чего выводит по каждой гласной букве количество раз, которое эта буква встретилась во введённой строке.
Можно попробовать решить задачу с использованием ООП, то есть ввести абстракцию, которая будет предоставлять функциональность подсчета статистики использования символов. Например, такую:
class SymStats
{
// Создает класс для подсчета статистики использования указанных
// символов
public SymStats(char[] charsForStats);
// Вычисляет статистику использования символов в данной строке
public void calculate(String s);
// Возвращает количество вхождений указанного символа
public int countChar(char c);
// Возвращает общее число подсчитываемых символов
public int countTotal();
// Выдает подсчитанную статистику на экран
public void writeStats();
}
2b. Баланс скобок.
Написать программу в процедурном стиле, которая вводит с консоли строку русского текста, после чего проверяет сбалансированность скобок (анализируются скобки 4 типов: круглые, фигурные, квадратные, угловые). В случае нарушения баланса (с учетом вложенности скобок!) требуется выдать информацию о месте возникновения ошибки.
В случае использования приемов ООП решение должно оформляться, например, посредством такого интерфейса:
interface StringValidator
{
// Проверяет строку на корректность
boolean isValid(String s);
// Если последний вызов isValid обнаружил ошибку, возвращает
// позицию, в которой обнаружена ошибка
int getErrorPos();
// Если последний вызов isValid обнаружил ошибку, возвращает
// сообщение об ошибке
String getErrorMessage();
}
С использованием такого интерфейса можно строить сложные правила проверки строк путем простого комбинирования реализаций этого интерфейса.
Также у этой задачи есть очень симпатичная вариация для сильных студентов: написать XML-валидатор (то есть проверяет правильность расстановки тегов с учетом парности и вложенности).
Классы как типы данных
Цели: познакомиться с модульным программированием; освоить шаблон “Неизменяемый класс”; освоить модульное тестирование и JUnit в частности.
3а. Вектор и отрезок
Реализовать неизменяемый класс Vector3D, представляющий вектор в 3-хмерном пространстве. У класса должны быть следующие методы:
// Получение координат
double getX();
double getY();
double getZ();
// Операции
double scalar(Vector3D v); // скалярное произведение векторов
double len(); // длина вектора
Vector3D multiply(double factor); // умножение на число
Vector3D add(Vector3D v); // сложение векторов
Vector3D sub(Vector3D v); // вычитание векторов
Класс вектора должен быть покрыт модульными тестами, сделанными на основе библиотеки JUnit.
Затем следует реализовать класс Segment, представляющий отрезок в 3-хмерном пространстве. У класса должны быть следующие методы:
Vector3D getStart();
Vector3D getEnd();
double len();
double distanceTo(Vector3D point);
При этом в реализации методов класса Segment запрещается использование методов Vector3D.getX(), Vector3D.getY(), Vector3D.getZ().
Теперь легко заметить, что класс Segment в своей работе не использует знание о размерности пространства. В резлуьтате его легко обобщить так, чтобы он работал с векторами любого пространства. Для этого нужно:
Ввести интерфейс Vector абстрактного вектора со следующими методами
int dimension(); // размерность
double getComponent(int i); // возвращает компоненту вектора
double scalar(Vector v);
double len();
Vector multiply(double factor);
Vector add(Vector v);
Vector sub(Vector v);
Указать, что класс Vector3D реализует этот интерфейс. Перевести класс Segment на использование Vector вместо Vector3D. Чтобы проверить, что Segment успешно обобщился, нужно создать реализацию вектора в 2-мерном пространстве Vector2D, и добавить соответствующих модульных тестов на класс Segment.
Наблюдение: в качестве усложнения можно рассматривать задачу переписывания этих классов на обобщенные типы (generics).
Полиморфизм, как способ расширения функциональности
Цели: познакомиться с работой в рамках существующего каркаса приложения; освоить полиморфизм, основанный на интерфейсах и абстрактных классах.
4а. Калькулятор
Требуется разобраться с каркасом существующего приложения (студент получает готовый набор исходников) и реализовать в рамках этого каркаса поддержку следующих типов данных: вектор, рациональное число, комплексное число.
Контейнеры
Цели:
- познакомиться с реализациями контейнерных классов;
- познакомиться с итераторами;
- познакомиться с обобщенными типами;
- познакомиться с с использованием исключений для сигнализации об ошибках в процессе работы программы;
5a. Стек
Требуется написать класс, реализующий структуру данных “стек”. Класс должен предоставлять доступ к элементам посредством итератора и быть совместимым с циклом for по итератору.
5б. Очередь
Требуется написать класс, реализующий структуру данных “очередь”. Класс должен предоставлять доступ к элементам посредством итератора и быть совместимым с циклом for по итератору.
5в. Динамический массив
Требуется написать класс, реализующий “массив переменного размера”. Класс должен предоставлять доступ к элементам посредством итератора и быть совместимым с циклом for по итератору.
Наблюдение: в качестве усложнения во всех трех вариантах можно рассматривать реализацию такого класса с использованием техники обобщенных типов (Generics). Очередь
Файловый ввод-вывод и кодировки
Цели: познакомиться с библиотеками ввода-вывода (файлы); научиться писать отказоустойчивые программы (грамотное использование исключений).
6а. Конвертор кодировок
Требуется написать программу, которая получает из командной строки 4 аргумента: имя исходного файла, имя исходной кодировки, имя выходного файла, имя выходной кодировки. Программа должна выполнять копирование указанного текстового файла, выполняя трансформацию кодировки.
Наблюдение: в качестве усложнения можно реализовать автоопределение входной кодировки на основе частотного анализа файла.
6b. Текстовый файл с произвольным доступом.
Требуется написать программу, которая позволила бы читать текстовые данные из произвольной позиции текстового файла (произвольный доступ организуется через класс RandomAccessFile). Можно предполагать, что исходный файл создан в кодировке UTF-8. Соответственно, при чтении надо убедиться, что с указанной позиции файла можно прочитать строку в UTF-8 (см. свойства кодировки; она позволяет выполнить синхронизацию для продолжения декодирования).
Исходники всех задач доступны на GitHub