Задачи

[ Java ]

26 Jun 2012

Задачи для практических занятий по учебному курсу “ООП” (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); // вычитание векторов

Исходник Vector3D

Класс вектора должен быть покрыт модульными тестами, сделанными на основе библиотеки JUnit.

Затем следует реализовать класс Segment, представляющий отрезок в 3-хмерном пространстве. У класса должны быть следующие методы:

 Vector3D getStart();
Vector3D getEnd();
double len();
double distanceTo(Vector3D point);

При этом в реализации методов класса Segment запрещается использование методов Vector3D.getX(), Vector3D.getY(), Vector3D.getZ().

Исходник Segment

Теперь легко заметить, что класс 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);

 

Интерфейс iVector

Указать, что класс Vector3D реализует этот интерфейс. Перевести класс Segment на использование Vector вместо Vector3D. Чтобы проверить, что Segment успешно обобщился, нужно создать реализацию вектора в 2-мерном пространстве Vector2D, и добавить соответствующих модульных тестов на класс Segment.

Исходник Vector2D

Исходник Vector

Исходник TestVector

Наблюдение: в качестве усложнения можно рассматривать задачу переписывания этих классов на обобщенные типы (generics).

Полиморфизм, как способ расширения функциональности

Цели: познакомиться с работой в рамках существующего каркаса приложения; освоить полиморфизм, основанный на интерфейсах и абстрактных классах.

4а. Калькулятор

Требуется разобраться с каркасом существующего приложения (студент получает готовый набор исходников) и реализовать в рамках этого каркаса поддержку следующих типов данных: вектор, рациональное число, комплексное число.

Контейнеры

Цели:

  • познакомиться с реализациями контейнерных классов;
  • познакомиться с итераторами;
  • познакомиться с обобщенными типами;
  • познакомиться с с использованием исключений для сигнализации об ошибках в процессе работы программы;

5a. Стек

Требуется написать класс, реализующий структуру данных “стек”. Класс должен предоставлять доступ к элементам посредством итератора и быть совместимым с циклом for по итератору.

5б. Очередь

Требуется написать класс, реализующий структуру данных “очередь”. Класс должен предоставлять доступ к элементам посредством итератора и быть совместимым с циклом for по итератору.

5в. Динамический массив

Требуется написать класс, реализующий “массив переменного размера”. Класс должен предоставлять доступ к элементам посредством итератора и быть совместимым с циклом for по итератору.

Наблюдение: в качестве усложнения во всех трех вариантах можно рассматривать реализацию такого класса с использованием техники обобщенных типов (Generics). Очередь

Файловый ввод-вывод и кодировки

Цели: познакомиться с библиотеками ввода-вывода (файлы); научиться писать отказоустойчивые программы (грамотное использование исключений).

6а. Конвертор кодировок

Требуется написать программу, которая получает из командной строки 4 аргумента: имя исходного файла, имя исходной кодировки, имя выходного файла, имя выходной кодировки. Программа должна выполнять копирование указанного текстового файла, выполняя трансформацию кодировки.

FileCoder

Наблюдение: в качестве усложнения можно реализовать автоопределение входной кодировки на основе частотного анализа файла.

6b. Текстовый файл с произвольным доступом.

Требуется написать программу, которая позволила бы читать текстовые данные из произвольной позиции текстового файла (произвольный доступ организуется через класс RandomAccessFile). Можно предполагать, что исходный файл создан в кодировке UTF-8. Соответственно, при чтении надо убедиться, что с указанной позиции файла можно прочитать строку в UTF-8 (см. свойства кодировки; она позволяет выполнить синхронизацию для продолжения декодирования).

Исходники всех задач доступны на GitHub