442 lines
13 KiB
Markdown
442 lines
13 KiB
Markdown
# Техническое задание: чтение и дозапись `Student` в CSV
|
|
|
|
## Задача
|
|
|
|
Реализовать два независимых класса для работы с объектами `Student` в формате CSV:
|
|
|
|
- `StudentCsvWriter` — дозапись студентов в файл;
|
|
- `StudentCsvReader` — последовательное чтение студентов из файла.
|
|
|
|
Для работы с форматом CSV использовать готовую библиотеку для работы с CSV. Самостоятельно реализовывать парсинг CSV запрещено.
|
|
|
|
Оба класса должны принимать **абсолютный путь к файлу**.
|
|
|
|
Исключения `Student` и `StudentBuilder` не обрабатывать.
|
|
|
|
---
|
|
|
|
# Формат CSV-файла
|
|
|
|
Первая строка файла должна содержать заголовок:
|
|
|
|
```text
|
|
groupNumber,averageGrade,recordBookNumber
|
|
```
|
|
|
|
Каждая следующая строка содержит данные одного студента:
|
|
|
|
```text
|
|
groupNumber,averageGrade,recordBookNumber
|
|
```
|
|
|
|
Пример:
|
|
|
|
```text
|
|
groupNumber,averageGrade,recordBookNumber
|
|
A12,4.5,10001
|
|
B07,3.8,10002
|
|
C25,4.9,10003
|
|
```
|
|
|
|
Для формирования и чтения CSV использовать стандартную библиотеку для работы с форматом CSV.
|
|
|
|
---
|
|
|
|
## `StudentCsvWriter`
|
|
|
|
### Назначение
|
|
|
|
Дозаписывать одного студента в **существующий** CSV-файл.
|
|
|
|
### Контракт
|
|
|
|
Конструктор:
|
|
|
|
```java
|
|
public StudentCsvWriter(String absoluteFilePath)
|
|
```
|
|
|
|
Метод:
|
|
|
|
```java
|
|
public void write(Student student) throws IOException
|
|
```
|
|
|
|
### Требования
|
|
|
|
1. Метод принимает одного `Student`.
|
|
2. Указанный файл должен уже существовать.
|
|
3. Запись выполняется в режиме добавления.
|
|
4. Существующие записи не должны изменяться.
|
|
5. Заголовок CSV не должен записываться повторно.
|
|
6. Если файл не существует, новый файл **не создавать**.
|
|
7. Если файл не существует, метод должен выбросить `IOException`.
|
|
8. При успешной записи метод завершается без исключения.
|
|
9. Метод не возвращает значение.
|
|
10. Исключения, связанные с записью файла, не обрабатывать внутри метода.
|
|
11. Исключения `Student` и `StudentBuilder` также не обрабатывать.
|
|
|
|
## Невозможность записи
|
|
|
|
Если указанный файл не существует, метод должен выбросить `IOException`.
|
|
|
|
Также `IOException` должен возникать и передаваться вызывающему коду, если:
|
|
|
|
- файл недоступен для записи;
|
|
- отсутствуют права на запись;
|
|
- произошла ошибка файловой системы;
|
|
- запись в файл невозможна по другой причине.
|
|
|
|
Создание файла с именем вида:
|
|
|
|
```text
|
|
students-yyyy-MM-dd-HH-mm.csv
|
|
```
|
|
|
|
`StudentCsvWriter` **не выполняет**.
|
|
|
|
## Пример теста: несуществующий файл
|
|
|
|
Передать путь к несуществующему файлу:
|
|
|
|
```java
|
|
writer.write(student);
|
|
```
|
|
|
|
Проверить:
|
|
|
|
```text
|
|
IOException
|
|
```
|
|
|
|
Проверить также, что новый файл не был создан.
|
|
|
|
## Изменение теста существующего файла
|
|
|
|
Для существующего файла:
|
|
|
|
```text
|
|
groupNumber,averageGrade,recordBookNumber
|
|
A12,4.5,10001
|
|
```
|
|
|
|
выполнить:
|
|
|
|
```java
|
|
writer.write(student2);
|
|
```
|
|
|
|
Проверить, что результат:
|
|
|
|
```text
|
|
groupNumber,averageGrade,recordBookNumber
|
|
A12,4.5,10001
|
|
B07,3.8,10002
|
|
```
|
|
|
|
Заголовок присутствует только один раз.
|
|
|
|
## Дополнительный метод `StudentCsvWriter`
|
|
|
|
Кроме метода записи одного студента:
|
|
|
|
```java
|
|
public void write(Student student) throws IOException;
|
|
```
|
|
|
|
необходимо реализовать метод:
|
|
|
|
```java
|
|
public void write(MyList<Student>[] students) throws IOException;
|
|
```
|
|
|
|
### Контракт
|
|
|
|
Метод должен:
|
|
|
|
1. Принимать массив `MyList<Student>`.
|
|
2. Последовательно обрабатывать все переданные коллекции.
|
|
3. Записывать каждого студента в указанный CSV-файл.
|
|
4. Сохранять порядок:
|
|
- коллекций в массиве;
|
|
- студентов внутри каждой `MyList`.
|
|
5. Использовать тот же формат CSV и те же правила записи, что и `write(Student)`.
|
|
6. Работать только с уже существующим файлом.
|
|
7. Не создавать файл, если он отсутствует.
|
|
8. При отсутствии файла выбрасывать `IOException`.
|
|
9. Не изменять переданные `MyList<Student>`.
|
|
10. Не обрабатывать исключения, возникающие при создании или валидации `Student`.
|
|
11. При ошибке записи прекращать операцию и передавать исключение вызывающему коду.
|
|
|
|
### Пример
|
|
|
|
```java
|
|
MyList<Student>[] studentLists = ...;
|
|
|
|
writer.write(studentLists);
|
|
```
|
|
|
|
Если:
|
|
|
|
```text
|
|
studentLists[0] = [Student A, Student B]
|
|
studentLists[1] = [Student C]
|
|
studentLists[2] = [Student D, Student E]
|
|
```
|
|
|
|
в файл должны последовательно добавиться:
|
|
|
|
```text
|
|
Student A
|
|
Student B
|
|
Student C
|
|
Student D
|
|
Student E
|
|
```
|
|
|
|
### Проверка пустых коллекций
|
|
|
|
Пустые `MyList<Student>` внутри массива не должны приводить к ошибке и не должны добавлять записей в файл.
|
|
|
|
### Проверка пустого массива
|
|
|
|
Для:
|
|
|
|
```java
|
|
MyList<Student>[] studentLists = ...; // размер 0
|
|
```
|
|
|
|
метод не должен добавлять никаких записей и не должен изменять файл.
|
|
|
|
### Проверка отсутствующего файла
|
|
|
|
Для несуществующего файла:
|
|
|
|
```java
|
|
writer.write(studentLists);
|
|
```
|
|
|
|
должен выбрасываться `IOException`. Файл создавать запрещено.
|
|
|
|
---
|
|
|
|
# `StudentCsvReader`
|
|
|
|
## Назначение
|
|
|
|
Последовательно читать студентов из CSV-файла.
|
|
|
|
## Контракт
|
|
|
|
Класс должен реализовывать:
|
|
|
|
```java
|
|
Iterator<Student>
|
|
```
|
|
|
|
Конструктор:
|
|
|
|
```java
|
|
public StudentCsvReader(String absoluteFilePath)
|
|
```
|
|
|
|
Методы:
|
|
|
|
```java
|
|
@Override
|
|
public boolean hasNext()
|
|
```
|
|
|
|
```java
|
|
@Override
|
|
public Student next()
|
|
```
|
|
|
|
### Требования к `hasNext()`
|
|
|
|
1. Проверяет наличие следующей записи `Student`.
|
|
2. Заголовок CSV не считается записью.
|
|
3. Повторный вызов `hasNext()` не должен пропускать следующую запись.
|
|
4. После окончания файла метод возвращает `false`.
|
|
|
|
### Требования к `next()`
|
|
|
|
1. Возвращает следующего `Student`.
|
|
2. `Student` создаётся через существующий `StudentBuilder`.
|
|
3. При отсутствии следующего элемента выбрасывается:
|
|
|
|
```text
|
|
NoSuchElementException
|
|
```
|
|
|
|
1. Весь файл не должен предварительно загружаться в коллекцию.
|
|
|
|
## Невозможность прочитать студента
|
|
|
|
Если следующую запись невозможно прочитать или преобразовать в `Student`, например:
|
|
|
|
- некорректный формат CSV;
|
|
- отсутствует обязательное поле;
|
|
- значение невозможно преобразовать в нужный тип;
|
|
- данные не проходят валидацию `Student`;
|
|
|
|
исключение должно передаваться вызывающему коду.
|
|
|
|
Исключения `Student` и `StudentBuilder` **не перехватывать и не обрабатывать**.
|
|
|
|
Некорректные записи не должны молча пропускаться.
|
|
|
|
---
|
|
|
|
# Общие требования
|
|
|
|
1. Оба класса принимают абсолютный путь к файлу.
|
|
2. Использовать готовую библиотеку для работы с CSV.
|
|
3. Не реализовывать собственный CSV-парсер.
|
|
4. Не использовать стандартные коллекции Java как основное хранилище студентов.
|
|
5. Не дублировать в этих классах валидацию `Student`.
|
|
6. Не дублировать логику создания `Student` вне `StudentBuilder`.
|
|
7. `StudentCsvWriter` отвечает только за запись.
|
|
8. `StudentCsvReader` отвечает только за чтение.
|
|
|
|
---
|
|
|
|
# Примеры тестов
|
|
|
|
## `StudentCsvWriter`
|
|
|
|
### Создание нового файла
|
|
|
|
Передать путь к несуществующему файлу и вызвать:
|
|
|
|
```java
|
|
writer.write(student);
|
|
```
|
|
|
|
Проверить:
|
|
|
|
- файл создан;
|
|
- имя соответствует `students-yyyy-MM-dd-HH-mm.csv`;
|
|
- присутствует заголовок;
|
|
- студент записан;
|
|
- метод завершился без исключения.
|
|
|
|
### Дозапись
|
|
|
|
Создать файл с одним студентом и выполнить:
|
|
|
|
```java
|
|
writer.write(student2);
|
|
```
|
|
|
|
Проверить, что:
|
|
|
|
- первая запись сохранилась;
|
|
- `student2` добавлен в конец;
|
|
- заголовок присутствует только один раз.
|
|
|
|
### Несколько последовательных записей
|
|
|
|
```java
|
|
writer.write(student1);
|
|
writer.write(student2);
|
|
writer.write(student3);
|
|
```
|
|
|
|
Проверить наличие трёх записей в правильном порядке.
|
|
|
|
### Невозможность записи
|
|
|
|
Передать путь, по которому невозможно создать или открыть файл для записи.
|
|
|
|
Проверить, что `write()` выбрасывает `IOException`.
|
|
|
|
Не проверять специальное возвращаемое значение — метод ничего не возвращает.
|
|
|
|
---
|
|
|
|
## `StudentCsvReader`
|
|
|
|
### Чтение корректного файла
|
|
|
|
Для файла:
|
|
|
|
```text
|
|
groupNumber,averageGrade,recordBookNumber
|
|
A12,4.5,10001
|
|
B07,3.8,10002
|
|
C25,4.9,10003
|
|
```
|
|
|
|
Проверить:
|
|
|
|
- `hasNext()` возвращает `true` до последнего студента;
|
|
- `next()` возвращает студентов в правильном порядке;
|
|
- после последнего студента `hasNext()` возвращает `false`.
|
|
|
|
### Повторный вызов `hasNext()`
|
|
|
|
```java
|
|
reader.hasNext();
|
|
reader.hasNext();
|
|
Student student = reader.next();
|
|
```
|
|
|
|
Проверить, что первый студент не был пропущен.
|
|
|
|
### `next()` после окончания файла
|
|
|
|
После чтения всех студентов вызвать:
|
|
|
|
```java
|
|
reader.next();
|
|
```
|
|
|
|
Проверить выброс:
|
|
|
|
```text
|
|
NoSuchElementException
|
|
```
|
|
|
|
### Некорректная запись
|
|
|
|
Передать файл:
|
|
|
|
```text
|
|
groupNumber,averageGrade,recordBookNumber
|
|
A12,4.5,10001
|
|
INVALID
|
|
C25,4.9,10003
|
|
```
|
|
|
|
Проверить, что ошибка при чтении некорректной записи передаётся вызывающему коду.
|
|
|
|
### Некорректные данные `Student`
|
|
|
|
Передать данные, нарушающие ограничения `Student`.
|
|
|
|
Проверить, что исключение от `Student` или `StudentBuilder` выходит наружу и не перехватывается `StudentCsvReader`.
|
|
|
|
---
|
|
|
|
# Названия файлов
|
|
|
|
```text
|
|
StudentCsvWriter.java
|
|
StudentCsvReader.java
|
|
```
|
|
|
|
# Git
|
|
|
|
Название ветки:
|
|
|
|
```text
|
|
feature/csv-student-io
|
|
```
|
|
|
|
Conventional Commit:
|
|
|
|
```text
|
|
feat: add csv student reader and writer
|
|
```
|