Merge branch 'docs/tasks'
This commit is contained in:
@@ -0,0 +1,455 @@
|
|||||||
|
# Техническое задание: поиск студентов с использованием паттерна Компоновщик
|
||||||
|
|
||||||
|
## Задача
|
||||||
|
|
||||||
|
Реализовать механизм поиска студентов по диапазону значений с использованием паттерна **Компоновщик**.
|
||||||
|
|
||||||
|
Должны поддерживаться следующие критерии поиска:
|
||||||
|
|
||||||
|
- диапазон `groupNumber`;
|
||||||
|
- диапазон `averageGrade`;
|
||||||
|
- диапазон `recordBookNumber`.
|
||||||
|
|
||||||
|
Критерии должны поддерживать объединение в составной критерий.
|
||||||
|
|
||||||
|
Режим объединения критериев — **объединение (ИЛИ)** или **пересечение (И)** — задаётся в конструкторе основного класса.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Названия классов и интерфейсов
|
||||||
|
|
||||||
|
## `StudentSearchCriteria`
|
||||||
|
|
||||||
|
Интерфейс отдельного критерия поиска.
|
||||||
|
|
||||||
|
### Контракт
|
||||||
|
|
||||||
|
```java
|
||||||
|
boolean matches(Student student);
|
||||||
|
```
|
||||||
|
|
||||||
|
Метод возвращает `true`, если студент соответствует критерию.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## `GroupNumberRangeCriteria`
|
||||||
|
|
||||||
|
Критерий поиска по диапазону `groupNumber`.
|
||||||
|
|
||||||
|
### Конструктор
|
||||||
|
|
||||||
|
```java
|
||||||
|
GroupNumberRangeCriteria(String min, String max);
|
||||||
|
```
|
||||||
|
|
||||||
|
### Метод
|
||||||
|
|
||||||
|
```java
|
||||||
|
boolean matches(Student student);
|
||||||
|
```
|
||||||
|
|
||||||
|
`groupNumber` имеет тип `String`, поэтому сравнение выполнять лексикографически.
|
||||||
|
|
||||||
|
Границы диапазона включаются.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## `AverageGradeRangeCriteria`
|
||||||
|
|
||||||
|
Критерий поиска по диапазону `averageGrade`.
|
||||||
|
|
||||||
|
### Конструктор
|
||||||
|
|
||||||
|
```java
|
||||||
|
AverageGradeRangeCriteria(double min, double max);
|
||||||
|
```
|
||||||
|
|
||||||
|
### Метод
|
||||||
|
|
||||||
|
```java
|
||||||
|
boolean matches(Student student);
|
||||||
|
```
|
||||||
|
|
||||||
|
Студент соответствует критерию, если:
|
||||||
|
|
||||||
|
```text
|
||||||
|
min <= averageGrade <= max
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## `RecordBookNumberRangeCriteria`
|
||||||
|
|
||||||
|
Критерий поиска по диапазону `recordBookNumber`.
|
||||||
|
|
||||||
|
### Конструктор
|
||||||
|
|
||||||
|
```java
|
||||||
|
RecordBookNumberRangeCriteria(int min, int max);
|
||||||
|
```
|
||||||
|
|
||||||
|
### Метод
|
||||||
|
|
||||||
|
```java
|
||||||
|
boolean matches(Student student);
|
||||||
|
```
|
||||||
|
|
||||||
|
Студент соответствует критерию, если:
|
||||||
|
|
||||||
|
```text
|
||||||
|
min <= recordBookNumber <= max
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# `CompositeStudentSearchCriteria`
|
||||||
|
|
||||||
|
Компоновщик критериев поиска.
|
||||||
|
|
||||||
|
Класс реализует:
|
||||||
|
|
||||||
|
```java
|
||||||
|
StudentSearchCriteria
|
||||||
|
```
|
||||||
|
|
||||||
|
и содержит другие реализации `StudentSearchCriteria`.
|
||||||
|
|
||||||
|
### Методы
|
||||||
|
|
||||||
|
```java
|
||||||
|
void add(StudentSearchCriteria criteria);
|
||||||
|
```
|
||||||
|
|
||||||
|
```java
|
||||||
|
void remove(StudentSearchCriteria criteria);
|
||||||
|
```
|
||||||
|
|
||||||
|
```java
|
||||||
|
boolean matches(Student student);
|
||||||
|
```
|
||||||
|
|
||||||
|
### Логика
|
||||||
|
|
||||||
|
`CompositeStudentSearchCriteria` должен поддерживать два режима:
|
||||||
|
|
||||||
|
```text
|
||||||
|
UNION — объединение условий, ИЛИ
|
||||||
|
INTERSECTION — пересечение условий, И
|
||||||
|
```
|
||||||
|
|
||||||
|
Режим должен задаваться при создании компоновщика:
|
||||||
|
|
||||||
|
```java
|
||||||
|
CompositeStudentSearchCriteria(
|
||||||
|
SearchOperation operation
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
где `SearchOperation` — перечисление:
|
||||||
|
|
||||||
|
```java
|
||||||
|
public enum SearchOperation {
|
||||||
|
UNION,
|
||||||
|
INTERSECTION
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Для `UNION` студент подходит, если выполняется **хотя бы один** вложенный критерий.
|
||||||
|
|
||||||
|
Для `INTERSECTION` студент подходит, если выполняются **все** вложенные критерии.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# `StudentSearchService`
|
||||||
|
|
||||||
|
Основной класс поиска.
|
||||||
|
|
||||||
|
Он должен принимать интерфейс `StudentSearchCriteria`, а не конкретную реализацию критерия.
|
||||||
|
|
||||||
|
### Конструктор
|
||||||
|
|
||||||
|
```java
|
||||||
|
StudentSearchService(StudentSearchCriteria criteria);
|
||||||
|
```
|
||||||
|
|
||||||
|
### Метод
|
||||||
|
|
||||||
|
```java
|
||||||
|
MyList<Student> find(MyList<Student> students);
|
||||||
|
```
|
||||||
|
|
||||||
|
### Поведение
|
||||||
|
|
||||||
|
Метод `find()` должен:
|
||||||
|
|
||||||
|
1. Последовательно проверить студентов исходной коллекции.
|
||||||
|
2. Передать каждого студента в:
|
||||||
|
|
||||||
|
```java
|
||||||
|
criteria.matches(student)
|
||||||
|
```
|
||||||
|
|
||||||
|
1. Добавить подходящих студентов в новую `MyList<Student>`.
|
||||||
|
2. Вернуть новую коллекцию.
|
||||||
|
|
||||||
|
`StudentSearchService` не должен содержать логику конкретных диапазонов или объединения критериев.
|
||||||
|
|
||||||
|
### Обработка отсутствующего критерия
|
||||||
|
|
||||||
|
Если `StudentSearchService` создан без критерия:
|
||||||
|
|
||||||
|
```java
|
||||||
|
new StudentSearchService(null);
|
||||||
|
```
|
||||||
|
|
||||||
|
необходимо выбросить:
|
||||||
|
|
||||||
|
```java
|
||||||
|
IllegalArgumentException
|
||||||
|
```
|
||||||
|
|
||||||
|
Класс не должен выполнять поиск без заданного критерия.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Пример использования
|
||||||
|
|
||||||
|
### Один критерий
|
||||||
|
|
||||||
|
```java
|
||||||
|
StudentSearchCriteria criteria =
|
||||||
|
new AverageGradeRangeCriteria(4.0, 5.0);
|
||||||
|
|
||||||
|
StudentSearchService service =
|
||||||
|
new StudentSearchService(criteria);
|
||||||
|
|
||||||
|
MyList<Student> result =
|
||||||
|
service.find(students);
|
||||||
|
```
|
||||||
|
|
||||||
|
### Пересечение критериев
|
||||||
|
|
||||||
|
Например, студент должен одновременно:
|
||||||
|
|
||||||
|
- находиться в группе от `A10` до `C30`;
|
||||||
|
- иметь средний балл от `4.0` до `5.0`.
|
||||||
|
|
||||||
|
```java
|
||||||
|
CompositeStudentSearchCriteria criteria =
|
||||||
|
new CompositeStudentSearchCriteria(
|
||||||
|
SearchOperation.INTERSECTION
|
||||||
|
);
|
||||||
|
|
||||||
|
criteria.add(
|
||||||
|
new GroupNumberRangeCriteria("A10", "C30")
|
||||||
|
);
|
||||||
|
|
||||||
|
criteria.add(
|
||||||
|
new AverageGradeRangeCriteria(4.0, 5.0)
|
||||||
|
);
|
||||||
|
|
||||||
|
StudentSearchService service =
|
||||||
|
new StudentSearchService(criteria);
|
||||||
|
```
|
||||||
|
|
||||||
|
### Объединение критериев
|
||||||
|
|
||||||
|
Студент должен соответствовать хотя бы одному условию:
|
||||||
|
|
||||||
|
```java
|
||||||
|
CompositeStudentSearchCriteria criteria =
|
||||||
|
new CompositeStudentSearchCriteria(
|
||||||
|
SearchOperation.UNION
|
||||||
|
);
|
||||||
|
|
||||||
|
criteria.add(
|
||||||
|
new AverageGradeRangeCriteria(4.5, 5.0)
|
||||||
|
);
|
||||||
|
|
||||||
|
criteria.add(
|
||||||
|
new RecordBookNumberRangeCriteria(10000, 20000)
|
||||||
|
);
|
||||||
|
|
||||||
|
StudentSearchService service =
|
||||||
|
new StudentSearchService(criteria);
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Общие требования
|
||||||
|
|
||||||
|
1. `StudentSearchService` должен работать только через `StudentSearchCriteria`.
|
||||||
|
2. `StudentSearchService` не должен знать о конкретных критериях.
|
||||||
|
3. `CompositeStudentSearchCriteria` должен реализовывать тот же интерфейс `StudentSearchCriteria`.
|
||||||
|
4. Должна поддерживаться вложенность компоновщиков.
|
||||||
|
|
||||||
|
Например:
|
||||||
|
|
||||||
|
```text
|
||||||
|
Composite
|
||||||
|
├── GroupNumberRangeCriteria
|
||||||
|
└── Composite
|
||||||
|
├── AverageGradeRangeCriteria
|
||||||
|
└── RecordBookNumberRangeCriteria
|
||||||
|
```
|
||||||
|
|
||||||
|
1. Исходную `MyList<Student>` не изменять.
|
||||||
|
2. Результат возвращать в виде новой `MyList<Student>`.
|
||||||
|
3. Порядок найденных студентов должен сохраняться.
|
||||||
|
4. Стандартные `List`, `ArrayList`, `LinkedList` не использовать.
|
||||||
|
5. Готовые методы поиска не использовать.
|
||||||
|
6. При отсутствии совпадений возвращать пустую `MyList<Student>`.
|
||||||
|
7. Для диапазонов `min > max` выбрасывать `IllegalArgumentException`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Примеры тестов
|
||||||
|
|
||||||
|
## `GroupNumberRangeCriteria`
|
||||||
|
|
||||||
|
Проверить:
|
||||||
|
|
||||||
|
- значение внутри диапазона;
|
||||||
|
- значение равное нижней границе;
|
||||||
|
- значение равное верхней границе;
|
||||||
|
- значение вне диапазона.
|
||||||
|
|
||||||
|
## `AverageGradeRangeCriteria`
|
||||||
|
|
||||||
|
Проверить:
|
||||||
|
|
||||||
|
```text
|
||||||
|
3.9 → false
|
||||||
|
4.0 → true
|
||||||
|
4.5 → true
|
||||||
|
5.0 → true
|
||||||
|
5.1 → false
|
||||||
|
```
|
||||||
|
|
||||||
|
## `RecordBookNumberRangeCriteria`
|
||||||
|
|
||||||
|
Проверить значения внутри и вне диапазона, включая обе границы.
|
||||||
|
|
||||||
|
## Некорректный диапазон
|
||||||
|
|
||||||
|
Для каждого критерия проверить:
|
||||||
|
|
||||||
|
```text
|
||||||
|
min > max
|
||||||
|
```
|
||||||
|
|
||||||
|
Ожидается:
|
||||||
|
|
||||||
|
```java
|
||||||
|
IllegalArgumentException
|
||||||
|
```
|
||||||
|
|
||||||
|
## `INTERSECTION`
|
||||||
|
|
||||||
|
Создать два критерия и проверить:
|
||||||
|
|
||||||
|
```text
|
||||||
|
критерий A = true
|
||||||
|
критерий B = true
|
||||||
|
→ true
|
||||||
|
```
|
||||||
|
|
||||||
|
```text
|
||||||
|
критерий A = true
|
||||||
|
критерий B = false
|
||||||
|
→ false
|
||||||
|
```
|
||||||
|
|
||||||
|
## `UNION`
|
||||||
|
|
||||||
|
Проверить:
|
||||||
|
|
||||||
|
```text
|
||||||
|
A = true
|
||||||
|
B = false
|
||||||
|
→ true
|
||||||
|
```
|
||||||
|
|
||||||
|
```text
|
||||||
|
A = false
|
||||||
|
B = true
|
||||||
|
→ true
|
||||||
|
```
|
||||||
|
|
||||||
|
```text
|
||||||
|
A = false
|
||||||
|
B = false
|
||||||
|
→ false
|
||||||
|
```
|
||||||
|
|
||||||
|
## Компоновщик без критериев
|
||||||
|
|
||||||
|
Проверить поведение:
|
||||||
|
|
||||||
|
```java
|
||||||
|
new CompositeStudentSearchCriteria(
|
||||||
|
SearchOperation.INTERSECTION
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
и:
|
||||||
|
|
||||||
|
```java
|
||||||
|
new CompositeStudentSearchCriteria(
|
||||||
|
SearchOperation.UNION
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
Поведение должно быть явно определено и закреплено тестами.
|
||||||
|
|
||||||
|
Рекомендуется:
|
||||||
|
|
||||||
|
- пустое `INTERSECTION` → `true`;
|
||||||
|
- пустое `UNION` → `false`.
|
||||||
|
|
||||||
|
## `StudentSearchService`
|
||||||
|
|
||||||
|
Проверить:
|
||||||
|
|
||||||
|
- корректный поиск по одному критерию;
|
||||||
|
- пустой результат;
|
||||||
|
- пустую исходную коллекцию;
|
||||||
|
- сохранение порядка;
|
||||||
|
- неизменность исходной коллекции;
|
||||||
|
- работу с `CompositeStudentSearchCriteria`.
|
||||||
|
|
||||||
|
## Проверка вложенного Компоновщика
|
||||||
|
|
||||||
|
Создать компоновщик с вложенным компоновщиком и проверить корректность вычисления результата.
|
||||||
|
|
||||||
|
## Проверка отсутствующего критерия
|
||||||
|
|
||||||
|
Создать:
|
||||||
|
|
||||||
|
```java
|
||||||
|
new StudentSearchService(null);
|
||||||
|
```
|
||||||
|
|
||||||
|
Проверить `IllegalArgumentException`.
|
||||||
|
|
||||||
|
## Результат
|
||||||
|
|
||||||
|
Реализовать:
|
||||||
|
|
||||||
|
```text
|
||||||
|
StudentSearchCriteria.java
|
||||||
|
GroupNumberRangeCriteria.java
|
||||||
|
AverageGradeRangeCriteria.java
|
||||||
|
RecordBookNumberRangeCriteria.java
|
||||||
|
CompositeStudentSearchCriteria.java
|
||||||
|
SearchOperation.java
|
||||||
|
StudentSearchService.java
|
||||||
|
```
|
||||||
|
|
||||||
|
## Git
|
||||||
|
|
||||||
|
Название ветки:
|
||||||
|
|
||||||
|
```text
|
||||||
|
feature/composite-student-search
|
||||||
|
```
|
||||||
@@ -0,0 +1,324 @@
|
|||||||
|
# Техническое задание: чтение и дозапись `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. Если файл не существует, необходимо создать файл.
|
||||||
|
6. При создании нового файла имя должно соответствовать формату:
|
||||||
|
|
||||||
|
```text
|
||||||
|
students-yyyy-MM-dd-HH-mm.csv
|
||||||
|
```
|
||||||
|
|
||||||
|
Например:
|
||||||
|
|
||||||
|
```text
|
||||||
|
students-2026-08-28-20-45.csv
|
||||||
|
```
|
||||||
|
|
||||||
|
1. Новый файл создаётся в директории, указанной абсолютным путём.
|
||||||
|
2. При создании нового файла сначала записывается заголовок.
|
||||||
|
3. При успешной записи метод завершается без исключения.
|
||||||
|
4. Метод не возвращает значение.
|
||||||
|
5. Исключения, связанные с записью файла, не обрабатывать внутри метода — они должны передаваться вызывающему коду.
|
||||||
|
6. Исключения `Student` и `StudentBuilder` также не обрабатывать.
|
||||||
|
|
||||||
|
## Невозможность записи
|
||||||
|
|
||||||
|
Если студент не может быть записан, например:
|
||||||
|
|
||||||
|
- отсутствуют права на запись;
|
||||||
|
- невозможно создать файл;
|
||||||
|
- файл недоступен;
|
||||||
|
- произошла ошибка файловой системы;
|
||||||
|
|
||||||
|
метод должен выбросить соответствующее исключение, например `IOException`.
|
||||||
|
|
||||||
|
Запись не должна возвращать специальное значение вроде `0` для обозначения ошибки.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# `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
|
||||||
|
```
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
# Техническое задание: кастомный контейнер List
|
||||||
|
|
||||||
|
## Задача
|
||||||
|
|
||||||
|
Реализовать собственный контейнер для хранения объектов, не используя готовые реализации `List`, `ArrayList` и `LinkedList`.
|
||||||
|
|
||||||
|
## Требования
|
||||||
|
|
||||||
|
1. Создать интерфейс `MyList<T>`.
|
||||||
|
|
||||||
|
2. В интерфейсе определить основные операции:
|
||||||
|
|
||||||
|
- `add(...)`;
|
||||||
|
- `get(...)`;
|
||||||
|
- `set(...)`;
|
||||||
|
- `remove(...)`;
|
||||||
|
- `size()`;
|
||||||
|
- `isEmpty()`.
|
||||||
|
|
||||||
|
3. Создать класс `MyLinkedList<T>`, реализующий `MyList<T>`.
|
||||||
|
|
||||||
|
4. Реализовать контейнер на основе собственных узлов `Node<T>`.
|
||||||
|
|
||||||
|
5. Контейнер должен поддерживать:
|
||||||
|
|
||||||
|
- добавление элемента;
|
||||||
|
- получение элемента по индексу;
|
||||||
|
- замену элемента по индексу;
|
||||||
|
- удаление элемента по индексу;
|
||||||
|
- получение количества элементов;
|
||||||
|
- проверку на пустоту.
|
||||||
|
|
||||||
|
6. Обработать некорректные индексы с помощью исключений.
|
||||||
|
|
||||||
|
7. Контейнер не должен зависеть от класса `Student`, сортировки или пользовательского интерфейса.
|
||||||
|
|
||||||
|
## Результат
|
||||||
|
|
||||||
|
Должны быть реализованы:
|
||||||
|
|
||||||
|
```text
|
||||||
|
MyList.java
|
||||||
|
MyLinkedList.java
|
||||||
|
```
|
||||||
@@ -0,0 +1,368 @@
|
|||||||
|
# Техническое задание: `EvenOnlySortStrategy`
|
||||||
|
|
||||||
|
## Задача
|
||||||
|
|
||||||
|
Создать класс `EvenOnlySortStrategy`, реализующий дополнительное задание по сортировке элементов с чётным значением выбранного числового поля.
|
||||||
|
|
||||||
|
Класс должен использовать **паттерн Стратегия**: он не реализует собственный алгоритм сортировки, а делегирует сортировку переданной реализации `SortStrategy`.
|
||||||
|
|
||||||
|
## Контракт
|
||||||
|
|
||||||
|
Класс:
|
||||||
|
|
||||||
|
```java
|
||||||
|
public class EvenOnlySortStrategy<T> implements SortStrategy<T>
|
||||||
|
```
|
||||||
|
|
||||||
|
Должен содержать два конструктора:
|
||||||
|
|
||||||
|
```java
|
||||||
|
public EvenOnlySortStrategy()
|
||||||
|
```
|
||||||
|
|
||||||
|
```java
|
||||||
|
public EvenOnlySortStrategy(SortStrategy<T> sortStrategy)
|
||||||
|
```
|
||||||
|
|
||||||
|
Также реализовать метод:
|
||||||
|
|
||||||
|
```java
|
||||||
|
public void setSortStrategy(SortStrategy<T> sortStrategy)
|
||||||
|
```
|
||||||
|
|
||||||
|
и основной метод интерфейса `SortStrategy`:
|
||||||
|
|
||||||
|
```java
|
||||||
|
@Override
|
||||||
|
public void sort(
|
||||||
|
MyList<T> list,
|
||||||
|
Comparator<T> comparator
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
Для определения чётности числового поля использовать:
|
||||||
|
|
||||||
|
```java
|
||||||
|
ToIntFunction<T>
|
||||||
|
```
|
||||||
|
|
||||||
|
Поэтому рекомендуется также передавать функцию получения числового значения через конструктор или отдельный метод настройки.
|
||||||
|
|
||||||
|
Для `Student` в качестве функции использовать:
|
||||||
|
|
||||||
|
```java
|
||||||
|
Student::getRecordBookNumber
|
||||||
|
```
|
||||||
|
|
||||||
|
## Поведение при отсутствии стратегии
|
||||||
|
|
||||||
|
Если `sortStrategy` не задан:
|
||||||
|
|
||||||
|
```java
|
||||||
|
EvenOnlySortStrategy<Student> strategy =
|
||||||
|
new EvenOnlySortStrategy<>();
|
||||||
|
```
|
||||||
|
|
||||||
|
то вызов:
|
||||||
|
|
||||||
|
```java
|
||||||
|
strategy.sort(students, comparator);
|
||||||
|
```
|
||||||
|
|
||||||
|
должен завершаться контролируемой ошибкой.
|
||||||
|
|
||||||
|
Рекомендуется выбрасывать:
|
||||||
|
|
||||||
|
```java
|
||||||
|
IllegalStateException
|
||||||
|
```
|
||||||
|
|
||||||
|
с понятным сообщением о том, что стратегия сортировки не задана.
|
||||||
|
|
||||||
|
Не следует использовать `null` как признак успешного выполнения сортировки.
|
||||||
|
|
||||||
|
## Поведение `setSortStrategy`
|
||||||
|
|
||||||
|
Метод:
|
||||||
|
|
||||||
|
```java
|
||||||
|
setSortStrategy(SortStrategy<T> sortStrategy)
|
||||||
|
```
|
||||||
|
|
||||||
|
заменяет текущую стратегию.
|
||||||
|
|
||||||
|
После вызова метода последующая сортировка должна выполняться именно переданной стратегией.
|
||||||
|
|
||||||
|
Передача `null` должна приводить к контролируемой ошибке, например:
|
||||||
|
|
||||||
|
```java
|
||||||
|
IllegalArgumentException
|
||||||
|
```
|
||||||
|
|
||||||
|
## Основная бизнес-логика
|
||||||
|
|
||||||
|
`EvenOnlySortStrategy` **не должен содержать код какого-либо алгоритма сортировки**.
|
||||||
|
|
||||||
|
Запрещено реализовывать внутри класса:
|
||||||
|
|
||||||
|
- Merge Sort;
|
||||||
|
- Bubble Sort;
|
||||||
|
- Selection Sort;
|
||||||
|
- другие алгоритмы сортировки;
|
||||||
|
- вызовы стандартных методов сортировки Java.
|
||||||
|
|
||||||
|
Класс отвечает только за:
|
||||||
|
|
||||||
|
1. определение элементов, участвующих в специальной сортировке;
|
||||||
|
2. сохранение исходных позиций нечётных элементов;
|
||||||
|
3. передачу подходящих элементов переданной `SortStrategy`;
|
||||||
|
4. восстановление результата на исходные позиции;
|
||||||
|
5. делегирование самого алгоритма сортировки.
|
||||||
|
|
||||||
|
Таким образом:
|
||||||
|
|
||||||
|
```text
|
||||||
|
EvenOnlySortStrategy
|
||||||
|
│
|
||||||
|
├── определяет, какие элементы сортировать
|
||||||
|
│
|
||||||
|
├── передаёт сортируемые элементы
|
||||||
|
│ ↓
|
||||||
|
│ SortStrategy
|
||||||
|
│ ↓
|
||||||
|
│ MergeSortStrategy
|
||||||
|
│ QuickSortStrategy
|
||||||
|
│ ...
|
||||||
|
│
|
||||||
|
└── возвращает результат на исходные позиции
|
||||||
|
```
|
||||||
|
|
||||||
|
Один `EvenOnlySortStrategy` должен работать с **любой реализацией `SortStrategy`**.
|
||||||
|
|
||||||
|
## Рекомендуемый алгоритм
|
||||||
|
|
||||||
|
Рекомендуется использовать следующий подход.
|
||||||
|
|
||||||
|
Исходная коллекция:
|
||||||
|
|
||||||
|
```text
|
||||||
|
[7, 8, 3, 2, 5, 4]
|
||||||
|
```
|
||||||
|
|
||||||
|
При выборе `recordBookNumber`:
|
||||||
|
|
||||||
|
```text
|
||||||
|
нечётные: 7, 3, 5
|
||||||
|
чётные: 8, 2, 4
|
||||||
|
```
|
||||||
|
|
||||||
|
1. Пройти исходный `MyList`.
|
||||||
|
2. С помощью `ToIntFunction<T>` определить чётность каждого элемента.
|
||||||
|
3. Запомнить позиции элементов с чётным значением.
|
||||||
|
4. Сформировать отдельный `MyList<T>` только из чётных элементов.
|
||||||
|
5. Передать эту коллекцию в `sortStrategy.sort(...)`.
|
||||||
|
6. Полученные отсортированные элементы последовательно вернуть на сохранённые позиции.
|
||||||
|
7. Элементы с нечётными значениями не изменять.
|
||||||
|
|
||||||
|
Например, переданная стратегия `MergeSortStrategy` сортирует:
|
||||||
|
|
||||||
|
```text
|
||||||
|
[8, 2, 4]
|
||||||
|
```
|
||||||
|
|
||||||
|
в:
|
||||||
|
|
||||||
|
```text
|
||||||
|
[2, 4, 8]
|
||||||
|
```
|
||||||
|
|
||||||
|
После возвращения элементов на исходные позиции:
|
||||||
|
|
||||||
|
```text
|
||||||
|
[7, 2, 3, 4, 5, 8]
|
||||||
|
```
|
||||||
|
|
||||||
|
При этом `EvenOnlySortStrategy` не знает, каким способом были отсортированы `8`, `2`, `4`.
|
||||||
|
|
||||||
|
## Работа с любой `SortStrategy`
|
||||||
|
|
||||||
|
Должно быть возможно использовать:
|
||||||
|
|
||||||
|
```java
|
||||||
|
EvenOnlySortStrategy<Student> strategy =
|
||||||
|
new EvenOnlySortStrategy<>(
|
||||||
|
new MergeSortStrategy<>(),
|
||||||
|
Student::getRecordBookNumber
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
А также после создания заменить стратегию:
|
||||||
|
|
||||||
|
```java
|
||||||
|
strategy.setSortStrategy(new AnotherSortStrategy<>());
|
||||||
|
```
|
||||||
|
|
||||||
|
После этого специальная сортировка должна использовать новую стратегию без изменения `EvenOnlySortStrategy`.
|
||||||
|
|
||||||
|
## Требования к `ToIntFunction`
|
||||||
|
|
||||||
|
Функция получения числового значения должна передаваться извне.
|
||||||
|
|
||||||
|
Для `Student`:
|
||||||
|
|
||||||
|
```java
|
||||||
|
Student::getRecordBookNumber
|
||||||
|
```
|
||||||
|
|
||||||
|
Стратегия не должна напрямую обращаться к:
|
||||||
|
|
||||||
|
```java
|
||||||
|
student.getRecordBookNumber()
|
||||||
|
```
|
||||||
|
|
||||||
|
или к другим полям `Student`.
|
||||||
|
|
||||||
|
Это позволяет использовать класс с другими типами объектов и другими числовыми полями.
|
||||||
|
|
||||||
|
## Требования к коллекциям
|
||||||
|
|
||||||
|
Работать необходимо с:
|
||||||
|
|
||||||
|
```java
|
||||||
|
MyList<T>
|
||||||
|
```
|
||||||
|
|
||||||
|
Использование `ArrayList`, `LinkedList` и других стандартных коллекций для хранения элементов запрещено.
|
||||||
|
|
||||||
|
Вспомогательная коллекция чётных элементов также должна быть `MyList<T>`.
|
||||||
|
|
||||||
|
## Тесты
|
||||||
|
|
||||||
|
### Переданная стратегия используется
|
||||||
|
|
||||||
|
Передать `MergeSortStrategy`.
|
||||||
|
|
||||||
|
Проверить, что сортировка чётных элементов выполняется переданной стратегией.
|
||||||
|
|
||||||
|
### Замена стратегии
|
||||||
|
|
||||||
|
1. Создать `EvenOnlySortStrategy` с одной реализацией `SortStrategy`.
|
||||||
|
2. Выполнить сортировку.
|
||||||
|
3. Вызвать `setSortStrategy(...)`.
|
||||||
|
4. Выполнить сортировку повторно.
|
||||||
|
5. Проверить, что используется новая стратегия.
|
||||||
|
|
||||||
|
### Конструктор без стратегии
|
||||||
|
|
||||||
|
Создать:
|
||||||
|
|
||||||
|
```java
|
||||||
|
new EvenOnlySortStrategy<>();
|
||||||
|
```
|
||||||
|
|
||||||
|
Вызвать `sort()`.
|
||||||
|
|
||||||
|
Проверить выброс `IllegalStateException`.
|
||||||
|
|
||||||
|
### Конструктор со стратегией
|
||||||
|
|
||||||
|
Создать объект с `MergeSortStrategy`.
|
||||||
|
|
||||||
|
Проверить успешное выполнение сортировки.
|
||||||
|
|
||||||
|
### Передача `null` в `setSortStrategy`
|
||||||
|
|
||||||
|
Проверить выброс `IllegalArgumentException`.
|
||||||
|
|
||||||
|
### Все элементы нечётные
|
||||||
|
|
||||||
|
```text
|
||||||
|
[7, 3, 5, 1]
|
||||||
|
```
|
||||||
|
|
||||||
|
Результат должен полностью совпадать с исходной коллекцией.
|
||||||
|
|
||||||
|
### Все элементы чётные
|
||||||
|
|
||||||
|
```text
|
||||||
|
[8, 2, 6, 4]
|
||||||
|
```
|
||||||
|
|
||||||
|
Ожидаемый результат:
|
||||||
|
|
||||||
|
```text
|
||||||
|
[2, 4, 6, 8]
|
||||||
|
```
|
||||||
|
|
||||||
|
### Смешанные значения
|
||||||
|
|
||||||
|
```text
|
||||||
|
[7, 8, 3, 2, 5, 4]
|
||||||
|
```
|
||||||
|
|
||||||
|
Ожидаемый результат:
|
||||||
|
|
||||||
|
```text
|
||||||
|
[7, 2, 3, 4, 5, 8]
|
||||||
|
```
|
||||||
|
|
||||||
|
### Повторяющиеся значения
|
||||||
|
|
||||||
|
Проверить корректность сортировки при наличии одинаковых чётных значений.
|
||||||
|
|
||||||
|
### Один элемент
|
||||||
|
|
||||||
|
Проверить отдельно чётный и нечётный элемент.
|
||||||
|
|
||||||
|
### Пустая коллекция
|
||||||
|
|
||||||
|
Сортировка не должна приводить к ошибке.
|
||||||
|
|
||||||
|
### Проверка сохранения нечётных элементов
|
||||||
|
|
||||||
|
Проверить не только значения, но и то, что объекты с нечётным `recordBookNumber` остались на тех же индексах.
|
||||||
|
|
||||||
|
### Проверка разных реализаций
|
||||||
|
|
||||||
|
Использовать `EvenOnlySortStrategy` с каждой доступной реализацией `SortStrategy`.
|
||||||
|
|
||||||
|
Проверить, что результат специальной сортировки соответствует контракту независимо от конкретного алгоритма.
|
||||||
|
|
||||||
|
### Проверка делегирования
|
||||||
|
|
||||||
|
Создать тестовую реализацию `SortStrategy`, которая фиксирует факт вызова `sort()`.
|
||||||
|
|
||||||
|
Передать её в `EvenOnlySortStrategy`.
|
||||||
|
|
||||||
|
Проверить, что:
|
||||||
|
|
||||||
|
- `sort()` был вызван;
|
||||||
|
- в стратегию передана коллекция только с чётными элементами;
|
||||||
|
- сама `EvenOnlySortStrategy` не выполняет сортировку самостоятельно.
|
||||||
|
|
||||||
|
## Результат
|
||||||
|
|
||||||
|
Реализовать:
|
||||||
|
|
||||||
|
```text
|
||||||
|
EvenOnlySortStrategy.java
|
||||||
|
```
|
||||||
|
|
||||||
|
Класс должен зависеть от абстракции:
|
||||||
|
|
||||||
|
```text
|
||||||
|
SortStrategy<T>
|
||||||
|
MyList<T>
|
||||||
|
Comparator<T>
|
||||||
|
ToIntFunction<T>
|
||||||
|
```
|
||||||
|
|
||||||
|
и **не должен содержать реализации конкретного алгоритма сортировки**.
|
||||||
|
|
||||||
|
## Git
|
||||||
|
|
||||||
|
Ветка:
|
||||||
|
|
||||||
|
```text
|
||||||
|
feature/even-only-sort-strategy
|
||||||
|
```
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
# Техническое задание: сортировка слиянием
|
||||||
|
|
||||||
|
## Задача
|
||||||
|
|
||||||
|
Реализовать паттерн Strategy и собственный алгоритм сортировки слиянием объектов `Student`.
|
||||||
|
|
||||||
|
## Требования
|
||||||
|
|
||||||
|
1. Создать интерфейс `SortStrategy<T>` с методом:
|
||||||
|
|
||||||
|
```text
|
||||||
|
sort(MyList<T> list, Comparator<T> comparator)
|
||||||
|
```
|
||||||
|
|
||||||
|
1. Создать класс `MergeSortStrategy<T>`, реализующий `SortStrategy<T>`.
|
||||||
|
|
||||||
|
2. Реализовать алгоритм сортировки слиянием самостоятельно.
|
||||||
|
|
||||||
|
3. Готовые методы сортировки Java не использовать.
|
||||||
|
|
||||||
|
4. Сортировка должна работать с `MyList<T>`.
|
||||||
|
|
||||||
|
5. Сортировка должна использовать переданный `Comparator<T>`.
|
||||||
|
|
||||||
|
6. Реализовать сортировку `Student` по каждому из трёх полей:
|
||||||
|
|
||||||
|
- `groupNumber`;
|
||||||
|
- `averageGrade`;
|
||||||
|
- `recordBookNumber`.
|
||||||
|
|
||||||
|
7. Создать отдельные компараторы:
|
||||||
|
|
||||||
|
- `StudentGroupComparator`;
|
||||||
|
- `StudentAverageGradeComparator`;
|
||||||
|
- `StudentRecordBookComparator`.
|
||||||
|
|
||||||
|
8. `MergeSortStrategy` не должен зависеть от класса `Student`.
|
||||||
|
|
||||||
|
## Результат
|
||||||
|
|
||||||
|
```text
|
||||||
|
SortStrategy.java
|
||||||
|
MergeSortStrategy.java
|
||||||
|
|
||||||
|
StudentGroupComparator.java
|
||||||
|
StudentAverageGradeComparator.java
|
||||||
|
StudentRecordBookComparator.java
|
||||||
|
```
|
||||||
@@ -0,0 +1,327 @@
|
|||||||
|
# Техническое задание: заполнение `MyList<Student>` посредством Stream API
|
||||||
|
|
||||||
|
## Задача
|
||||||
|
|
||||||
|
Реализовать механизм заполнения собственной коллекции `MyList<Student>` посредством Java Stream API без использования стандартных коллекций Java в качестве промежуточного или итогового хранилища.
|
||||||
|
|
||||||
|
## Требования
|
||||||
|
|
||||||
|
1. Итоговым результатом работы потока должна быть:
|
||||||
|
|
||||||
|
```java
|
||||||
|
MyList<Student>
|
||||||
|
```
|
||||||
|
|
||||||
|
1. Для сбора элементов потока непосредственно в `MyList<Student>` реализовать собственный `Collector`.
|
||||||
|
|
||||||
|
2. Стандартные коллекции Java (`List`, `ArrayList`, `LinkedList` и другие реализации `List`) запрещено использовать:
|
||||||
|
|
||||||
|
- как итоговую коллекцию;
|
||||||
|
- как промежуточное хранилище элементов перед их переносом в `MyList`.
|
||||||
|
|
||||||
|
3. `Student` должен создаваться только через уже существующий независимый класс `StudentBuilder`, если объект создаётся первоначально.
|
||||||
|
|
||||||
|
4. `MyListCollector` не должен создавать или изменять объекты `Student`. Он работает с уже созданными объектами.
|
||||||
|
|
||||||
|
5. При использовании `map` для получения нового `Student` использовать методы `with...()`, а не `StudentBuilder`.
|
||||||
|
|
||||||
|
6. Логика заполнения `MyList` должна быть отделена от логики класса `Student`.
|
||||||
|
|
||||||
|
7. Для добавления объектов использовать существующий метод:
|
||||||
|
|
||||||
|
```java
|
||||||
|
add(Student student)
|
||||||
|
```
|
||||||
|
|
||||||
|
1. Необходимо реализовать поддержку объединения частичных результатов, необходимую для работы `Collector`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## `MyListCollector<T>`
|
||||||
|
|
||||||
|
Создать универсальный класс:
|
||||||
|
|
||||||
|
```java
|
||||||
|
public class MyListCollector<T>
|
||||||
|
implements Collector<T, MyList<T>, MyList<T>> {
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Реализовать методы:
|
||||||
|
|
||||||
|
```text
|
||||||
|
supplier()
|
||||||
|
accumulator()
|
||||||
|
combiner()
|
||||||
|
finisher()
|
||||||
|
characteristics()
|
||||||
|
```
|
||||||
|
|
||||||
|
Назначение:
|
||||||
|
|
||||||
|
- `supplier()` — создать пустой `MyList<T>`;
|
||||||
|
- `accumulator()` — добавить очередной элемент в `MyList<T>`;
|
||||||
|
- `combiner()` — объединить две частично заполненные коллекции;
|
||||||
|
- `finisher()` — вернуть готовый `MyList<T>`;
|
||||||
|
- `characteristics()` — определить характеристики сборщика.
|
||||||
|
|
||||||
|
Коллектор должен работать с `Student` без специальной логики, связанной с его полями.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## `MyCollectors`
|
||||||
|
|
||||||
|
Создать вспомогательный класс:
|
||||||
|
|
||||||
|
```java
|
||||||
|
public final class MyCollectors {
|
||||||
|
|
||||||
|
public static <T> Collector<T, MyList<T>, MyList<T>> toMyList() {
|
||||||
|
return new MyListCollector<>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Класс должен предоставлять удобный способ получения собственного сборщика.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Контракт использования
|
||||||
|
|
||||||
|
Должна поддерживаться конструкция:
|
||||||
|
|
||||||
|
```java
|
||||||
|
MyList<Student> students = Stream.of(
|
||||||
|
student1,
|
||||||
|
student2,
|
||||||
|
student3
|
||||||
|
).collect(MyCollectors.toMyList());
|
||||||
|
```
|
||||||
|
|
||||||
|
где `student1`, `student2`, `student3` — валидные объекты `Student`, предварительно созданные через `StudentBuilder`.
|
||||||
|
|
||||||
|
Результатом должен быть именно `MyList<Student>`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Пример `map`
|
||||||
|
|
||||||
|
`map` должен использоваться для преобразования одного `Student` в новый `Student`.
|
||||||
|
|
||||||
|
Исходный объект не изменяется. Для создания нового объекта использовать `with...()`.
|
||||||
|
|
||||||
|
Одно из преобразуемых полей должно быть `String` и формироваться по заданному бизнес-правилу:
|
||||||
|
|
||||||
|
```text
|
||||||
|
Заглавная латинская буква + двузначное число
|
||||||
|
```
|
||||||
|
|
||||||
|
Например:
|
||||||
|
|
||||||
|
```text
|
||||||
|
A07
|
||||||
|
B12
|
||||||
|
Z35
|
||||||
|
```
|
||||||
|
|
||||||
|
Пример:
|
||||||
|
|
||||||
|
```java
|
||||||
|
MyList<Student> students = Stream.of(
|
||||||
|
student1,
|
||||||
|
student2,
|
||||||
|
student3
|
||||||
|
)
|
||||||
|
.map(student -> student.withGroupNumber(
|
||||||
|
generateGroupNumber(student)
|
||||||
|
))
|
||||||
|
.collect(MyCollectors.toMyList());
|
||||||
|
```
|
||||||
|
|
||||||
|
Пример функции формирования значения:
|
||||||
|
|
||||||
|
```java
|
||||||
|
private static String generateGroupNumber(Student student) {
|
||||||
|
int number = Math.abs(student.getRecordBookNumber().hashCode() % 100);
|
||||||
|
char letter = (char) ('A'
|
||||||
|
+ Math.abs(student.getRecordBookNumber().hashCode()) % 26);
|
||||||
|
|
||||||
|
return String.format("%c%02d", letter, number);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Результат `map` должен оставаться `Stream<Student>`, после чего он непосредственно собирается в `MyList<Student>`.
|
||||||
|
|
||||||
|
Формат создаваемого `groupNumber` должен соответствовать:
|
||||||
|
|
||||||
|
```text
|
||||||
|
^[A-Z][0-9]{2}$
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Пример `filter` совместно с `map`
|
||||||
|
|
||||||
|
Проверить совместную работу промежуточных операций:
|
||||||
|
|
||||||
|
```java
|
||||||
|
MyList<Student> students = Stream.of(
|
||||||
|
student1,
|
||||||
|
student2,
|
||||||
|
student3
|
||||||
|
)
|
||||||
|
.filter(student -> student.getAverageGrade() >= 4.0)
|
||||||
|
.map(student -> student.withGroupNumber(
|
||||||
|
generateGroupNumber(student)
|
||||||
|
))
|
||||||
|
.collect(MyCollectors.toMyList());
|
||||||
|
```
|
||||||
|
|
||||||
|
Проверить, что:
|
||||||
|
|
||||||
|
- в результат попали только студенты со средним баллом не ниже `4.0`;
|
||||||
|
- каждый результат является новым `Student`;
|
||||||
|
- `groupNumber` соответствует формату `[A-Z][0-9]{2}`;
|
||||||
|
- исходные объекты не изменились.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Параллельные потоки
|
||||||
|
|
||||||
|
`combiner()` должен корректно объединять две частично сформированные `MyList<Student>`.
|
||||||
|
|
||||||
|
Реалистичный пример:
|
||||||
|
|
||||||
|
```java
|
||||||
|
MyList<Student> students = IntStream.range(0, 10_000)
|
||||||
|
.parallel()
|
||||||
|
.mapToObj(i -> student1.withGroupNumber(
|
||||||
|
String.format("%c%02d",
|
||||||
|
'A' + (i % 26),
|
||||||
|
i % 100)
|
||||||
|
))
|
||||||
|
.collect(MyCollectors.toMyList());
|
||||||
|
```
|
||||||
|
|
||||||
|
Проверить:
|
||||||
|
|
||||||
|
- результат содержит `10_000` объектов;
|
||||||
|
- ни один объект не потерян;
|
||||||
|
- количество объектов не превышает ожидаемого;
|
||||||
|
- каждый `groupNumber` соответствует формату `[A-Z][0-9]{2}`;
|
||||||
|
- объекты `Student` не изменяются после создания;
|
||||||
|
- `combiner()` корректно объединяет частичные результаты.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Примеры тестов
|
||||||
|
|
||||||
|
### Заполнение несколькими `Student`
|
||||||
|
|
||||||
|
Создать три валидных объекта `Student` через `StudentBuilder` и собрать их:
|
||||||
|
|
||||||
|
```java
|
||||||
|
MyList<Student> result = Stream.of(
|
||||||
|
student1,
|
||||||
|
student2,
|
||||||
|
student3
|
||||||
|
).collect(MyCollectors.toMyList());
|
||||||
|
```
|
||||||
|
|
||||||
|
Проверить:
|
||||||
|
|
||||||
|
```text
|
||||||
|
result != null
|
||||||
|
result.size() == 3
|
||||||
|
```
|
||||||
|
|
||||||
|
и соответствие элементов исходному потоку.
|
||||||
|
|
||||||
|
### Проверка порядка
|
||||||
|
|
||||||
|
Для:
|
||||||
|
|
||||||
|
```text
|
||||||
|
student1
|
||||||
|
student2
|
||||||
|
student3
|
||||||
|
```
|
||||||
|
|
||||||
|
проверить:
|
||||||
|
|
||||||
|
```text
|
||||||
|
result.get(0) == student1
|
||||||
|
result.get(1) == student2
|
||||||
|
result.get(2) == student3
|
||||||
|
```
|
||||||
|
|
||||||
|
### Пустой поток
|
||||||
|
|
||||||
|
```java
|
||||||
|
MyList<Student> result = Stream.<Student>empty()
|
||||||
|
.collect(MyCollectors.toMyList());
|
||||||
|
```
|
||||||
|
|
||||||
|
Проверить:
|
||||||
|
|
||||||
|
```text
|
||||||
|
result != null
|
||||||
|
result.isEmpty()
|
||||||
|
```
|
||||||
|
|
||||||
|
### Проверка `map`
|
||||||
|
|
||||||
|
Преобразовать несколько `Student` через `withGroupNumber()`.
|
||||||
|
|
||||||
|
Проверить:
|
||||||
|
|
||||||
|
- исходные объекты не изменились;
|
||||||
|
- созданные объекты отличаются от исходных;
|
||||||
|
- `groupNumber` соответствует формату `[A-Z][0-9]{2}`.
|
||||||
|
|
||||||
|
### Проверка `filter` и `map`
|
||||||
|
|
||||||
|
Проверить, что после `filter` и `map` в `MyList<Student>` попадают только необходимые студенты с корректно сформированным `groupNumber`.
|
||||||
|
|
||||||
|
### Параллельный поток
|
||||||
|
|
||||||
|
Создать поток из `10_000` объектов через `parallel()`.
|
||||||
|
|
||||||
|
Проверить размер итоговой коллекции, отсутствие потери элементов и корректность `groupNumber`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Ограничения
|
||||||
|
|
||||||
|
Запрещено:
|
||||||
|
|
||||||
|
- использовать `Collectors.toList()`;
|
||||||
|
- использовать `ArrayList`, `LinkedList` или другие стандартные коллекции для накопления результата;
|
||||||
|
- сначала собирать `Student` в стандартную коллекцию, а затем переносить их в `MyList`;
|
||||||
|
- создавать `Student` внутри `MyListCollector`;
|
||||||
|
- изменять существующий `Student`;
|
||||||
|
- использовать `StudentBuilder` внутри `map` вместо `with...()`.
|
||||||
|
|
||||||
|
## Результат
|
||||||
|
|
||||||
|
Реализовать:
|
||||||
|
|
||||||
|
```text
|
||||||
|
MyListCollector.java
|
||||||
|
MyCollectors.java
|
||||||
|
```
|
||||||
|
|
||||||
|
Использовать существующие:
|
||||||
|
|
||||||
|
```text
|
||||||
|
Student.java
|
||||||
|
StudentBuilder.java
|
||||||
|
MyList.java
|
||||||
|
MyLinkedList.java
|
||||||
|
```
|
||||||
|
|
||||||
|
### Рекомендация по архитектуре
|
||||||
|
|
||||||
|
`MyListCollector` должен зависеть только от интерфейса `MyList<T>`, а не от конкретной реализации `MyLinkedList<T>`.
|
||||||
|
|
||||||
|
`MyCollectors` должен содержать только методы создания собственных сборщиков и не должен содержать бизнес-логику.
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
# Техническое задание: класс `Student`
|
||||||
|
|
||||||
|
## Задача
|
||||||
|
|
||||||
|
Реализовать класс `Student` как единственную точку проверки корректности данных. Объект класса `Student` не может быть создан с некорректными данными.
|
||||||
|
|
||||||
|
## Требования
|
||||||
|
|
||||||
|
1. Класс `Student` содержит три поля:
|
||||||
|
|
||||||
|
- `groupNumber`;
|
||||||
|
- `averageGrade`;
|
||||||
|
- `recordBookNumber`.
|
||||||
|
|
||||||
|
2. Все поля должны быть `private` и неизменяемыми после создания объекта.
|
||||||
|
|
||||||
|
3. Проверка корректности данных должна находиться в классе `Student`.
|
||||||
|
|
||||||
|
4. Реализовать класс `StudentBuilder` для создания объектов `Student`.
|
||||||
|
|
||||||
|
5. В `StudentBuilder` реализовать методы:
|
||||||
|
|
||||||
|
- `setGroupNumber(...)`;
|
||||||
|
- `setAverageGrade(...)`;
|
||||||
|
- `setRecordBookNumber(...)`;
|
||||||
|
- `build()`.
|
||||||
|
|
||||||
|
6. Метод `build()` должен создавать только корректный объект `Student`. При некорректных данных должно выбрасываться исключение.
|
||||||
|
|
||||||
|
7. Реализовать для `Student`:
|
||||||
|
|
||||||
|
- методы получения значений полей;
|
||||||
|
- `toString()`;
|
||||||
|
- `equals()`;
|
||||||
|
- `hashCode()`.
|
||||||
|
|
||||||
|
## Результат
|
||||||
|
|
||||||
|
Должны быть реализованы два независимых класса:
|
||||||
|
|
||||||
|
```text
|
||||||
|
Student.java
|
||||||
|
StudentBuilder.java
|
||||||
|
```
|
||||||
|
|
||||||
|
Классы не должны зависеть от сортировки, коллекции, меню и ввода данных.
|
||||||
@@ -0,0 +1,688 @@
|
|||||||
|
# Техническое задание: многопоточный поиск индексов `Student`
|
||||||
|
|
||||||
|
## Задача
|
||||||
|
|
||||||
|
Реализовать многопоточный поиск всех вхождений заданного объекта `Student` в `MyList<Student>`.
|
||||||
|
|
||||||
|
Метод **не должен выводить никаких сообщений в консоль**.
|
||||||
|
|
||||||
|
Результатом работы должен быть массив `int[]`, содержащий индексы всех элементов исходной коллекции, которые равны переданному `Student`.
|
||||||
|
|
||||||
|
Сравнение выполнять через существующий `Student.equals()`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Входная информация
|
||||||
|
|
||||||
|
Основной метод принимает:
|
||||||
|
|
||||||
|
```java
|
||||||
|
MyList<Student> students
|
||||||
|
```
|
||||||
|
|
||||||
|
Исходная коллекция студентов.
|
||||||
|
|
||||||
|
```java
|
||||||
|
Student target
|
||||||
|
```
|
||||||
|
|
||||||
|
Студент, вхождения которого необходимо найти.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Выходная информация
|
||||||
|
|
||||||
|
Метод возвращает:
|
||||||
|
|
||||||
|
```java
|
||||||
|
int[]
|
||||||
|
```
|
||||||
|
|
||||||
|
Массив должен содержать индексы всех совпавших элементов.
|
||||||
|
|
||||||
|
Пример:
|
||||||
|
|
||||||
|
```text
|
||||||
|
Исходная коллекция:
|
||||||
|
|
||||||
|
[Student A, Student B, Student A, Student C, Student A]
|
||||||
|
|
||||||
|
Результат:
|
||||||
|
|
||||||
|
[0, 2, 4]
|
||||||
|
```
|
||||||
|
|
||||||
|
Индексы должны быть расположены в порядке возрастания.
|
||||||
|
|
||||||
|
При отсутствии совпадений:
|
||||||
|
|
||||||
|
```text
|
||||||
|
[]
|
||||||
|
```
|
||||||
|
|
||||||
|
При пустой коллекции:
|
||||||
|
|
||||||
|
```text
|
||||||
|
[]
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Название класса
|
||||||
|
|
||||||
|
```text
|
||||||
|
StudentOccurrenceIndexFinder
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Публичные методы и контракты
|
||||||
|
|
||||||
|
## `findOccurrences`
|
||||||
|
|
||||||
|
```java
|
||||||
|
public int[] findOccurrences(
|
||||||
|
MyList<Student> students,
|
||||||
|
Student target
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
Метод должен:
|
||||||
|
|
||||||
|
1. Проверить входные параметры.
|
||||||
|
2. Разделить коллекцию на диапазоны индексов.
|
||||||
|
3. Передать каждый диапазон отдельной задаче.
|
||||||
|
4. Выполнить поиск в нескольких потоках.
|
||||||
|
5. Получить от каждого потока найденные индексы.
|
||||||
|
6. Объединить результаты.
|
||||||
|
7. Вернуть индексы в порядке возрастания.
|
||||||
|
8. Не изменять исходную коллекцию.
|
||||||
|
9. Не изменять объекты `Student`.
|
||||||
|
10. Не выводить ничего в консоль.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Вспомогательные методы
|
||||||
|
|
||||||
|
Количество и вид `private` методов могут быть выбраны разработчиком.
|
||||||
|
|
||||||
|
Рекомендуется выделить:
|
||||||
|
|
||||||
|
```java
|
||||||
|
private int calculateThreadCount(int size);
|
||||||
|
```
|
||||||
|
|
||||||
|
Определяет количество рабочих потоков.
|
||||||
|
|
||||||
|
```java
|
||||||
|
private int countChunkSize(int size, int threadCount);
|
||||||
|
```
|
||||||
|
|
||||||
|
Определяет размер диапазона для одной задачи.
|
||||||
|
|
||||||
|
```java
|
||||||
|
private MyList<Integer> findInRange(
|
||||||
|
MyList<Student> students,
|
||||||
|
Student target,
|
||||||
|
int fromIndex,
|
||||||
|
int toIndex
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
Обрабатывает диапазон:
|
||||||
|
|
||||||
|
```text
|
||||||
|
[fromIndex, toIndex)
|
||||||
|
```
|
||||||
|
|
||||||
|
и возвращает индексы найденных совпадений.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Работа с многопоточностью
|
||||||
|
|
||||||
|
## Рекомендуемый подход
|
||||||
|
|
||||||
|
Не использовать общий контейнер результатов, к которому одновременно обращаются несколько потоков.
|
||||||
|
|
||||||
|
Также **не рекомендуется использовать мьютексы** (`synchronized`, `Lock`, `ReentrantLock`) для хранения результата.
|
||||||
|
|
||||||
|
Лучше использовать модель:
|
||||||
|
|
||||||
|
```text
|
||||||
|
MyList<Student>
|
||||||
|
│
|
||||||
|
┌────────────┼────────────┐
|
||||||
|
↓ ↓ ↓
|
||||||
|
Поток 1 Поток 2 Поток 3
|
||||||
|
диапазон диапазон диапазон
|
||||||
|
│ │ │
|
||||||
|
↓ ↓ ↓
|
||||||
|
MyList<Integer> MyList<Integer> MyList<Integer>
|
||||||
|
└────────────┼────────────┘
|
||||||
|
↓
|
||||||
|
основной поток
|
||||||
|
↓
|
||||||
|
merge
|
||||||
|
↓
|
||||||
|
int[]
|
||||||
|
```
|
||||||
|
|
||||||
|
Каждый поток создаёт **собственный контейнер `MyList<Integer>`** и записывает найденные индексы только в него.
|
||||||
|
|
||||||
|
Это позволяет избежать состояния гонки без синхронизации общей структуры.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Разбиение коллекции на потоки
|
||||||
|
|
||||||
|
Количество потоков рекомендуется определять на основании:
|
||||||
|
|
||||||
|
```java
|
||||||
|
Runtime.getRuntime().availableProcessors()
|
||||||
|
```
|
||||||
|
|
||||||
|
Количество потоков не должно превышать количество элементов:
|
||||||
|
|
||||||
|
```text
|
||||||
|
threadCount = min(
|
||||||
|
availableProcessors(),
|
||||||
|
students.size()
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Пример
|
||||||
|
|
||||||
|
Для коллекции из `10000` элементов и четырёх рабочих потоков:
|
||||||
|
|
||||||
|
```text
|
||||||
|
Поток 1 → [0, 2500)
|
||||||
|
Поток 2 → [2500, 5000)
|
||||||
|
Поток 3 → [5000, 7500)
|
||||||
|
Поток 4 → [7500, 10000)
|
||||||
|
```
|
||||||
|
|
||||||
|
Каждый индекс должен принадлежать **ровно одному диапазону**.
|
||||||
|
|
||||||
|
Не допускаются:
|
||||||
|
|
||||||
|
- пропущенные индексы;
|
||||||
|
- обработка одного индекса несколькими потоками.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Поиск внутри потока
|
||||||
|
|
||||||
|
Каждая задача обрабатывает только свой диапазон.
|
||||||
|
|
||||||
|
Для каждого индекса:
|
||||||
|
|
||||||
|
```java
|
||||||
|
Student student = students.get(index);
|
||||||
|
```
|
||||||
|
|
||||||
|
затем:
|
||||||
|
|
||||||
|
```java
|
||||||
|
target.equals(student)
|
||||||
|
```
|
||||||
|
|
||||||
|
Если результат `true`, индекс добавляется в локальный:
|
||||||
|
|
||||||
|
```java
|
||||||
|
MyList<Integer>
|
||||||
|
```
|
||||||
|
|
||||||
|
Пример:
|
||||||
|
|
||||||
|
```text
|
||||||
|
Поток 1 → [0, 8, 15]
|
||||||
|
Поток 2 → [21, 24]
|
||||||
|
Поток 3 → [30, 42, 48]
|
||||||
|
Поток 4 → [63]
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Объединение результатов
|
||||||
|
|
||||||
|
Основной поток после завершения всех задач должен получить локальные результаты и объединить их.
|
||||||
|
|
||||||
|
Рекомендуется передавать задачи в порядке возрастания диапазонов:
|
||||||
|
|
||||||
|
```text
|
||||||
|
Результат 1 → диапазон 0
|
||||||
|
Результат 2 → диапазон 1
|
||||||
|
Результат 3 → диапазон 2
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
Если каждая задача добавляет индексы в возрастающем порядке, а результаты объединяются в порядке диапазонов, итоговый массив уже будет отсортирован:
|
||||||
|
|
||||||
|
```text
|
||||||
|
[0, 8, 15, 21, 24, 30, 42, 48, 63]
|
||||||
|
```
|
||||||
|
|
||||||
|
В этом случае дополнительная сортировка результата не требуется.
|
||||||
|
|
||||||
|
**`Arrays.sort()` и другие готовые методы сортировки использовать не следует**, поскольку порядок можно гарантировать самим алгоритмом объединения.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Потокобезопасность
|
||||||
|
|
||||||
|
## Мьютексы
|
||||||
|
|
||||||
|
Использование мьютексов для результата **не требуется**.
|
||||||
|
|
||||||
|
Не рекомендуется использовать:
|
||||||
|
|
||||||
|
```java
|
||||||
|
synchronized
|
||||||
|
```
|
||||||
|
|
||||||
|
```java
|
||||||
|
Lock
|
||||||
|
```
|
||||||
|
|
||||||
|
```java
|
||||||
|
ReentrantLock
|
||||||
|
```
|
||||||
|
|
||||||
|
```java
|
||||||
|
AtomicInteger
|
||||||
|
```
|
||||||
|
|
||||||
|
для общего результата.
|
||||||
|
|
||||||
|
Причина: потоки не должны изменять одну общую структуру.
|
||||||
|
|
||||||
|
Каждый поток работает со своим:
|
||||||
|
|
||||||
|
```text
|
||||||
|
MyList<Integer>
|
||||||
|
```
|
||||||
|
|
||||||
|
а объединение выполняется после завершения параллельной части одним потоком.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Рекомендуемые классы и импорты
|
||||||
|
|
||||||
|
Для реализации рекомендуется использовать стандартные средства Java:
|
||||||
|
|
||||||
|
```java
|
||||||
|
import java.util.concurrent.Callable;
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
import java.util.concurrent.Future;
|
||||||
|
```
|
||||||
|
|
||||||
|
Рекомендуемая схема:
|
||||||
|
|
||||||
|
```text
|
||||||
|
ExecutorService
|
||||||
|
↓
|
||||||
|
Callable<MyList<Integer>>
|
||||||
|
↓
|
||||||
|
Future<MyList<Integer>>
|
||||||
|
```
|
||||||
|
|
||||||
|
Каждый `Callable` обрабатывает свой диапазон и возвращает собственный `MyList<Integer>`.
|
||||||
|
|
||||||
|
После получения всех `Future` основной метод объединяет результаты.
|
||||||
|
|
||||||
|
После завершения работы `ExecutorService` должен быть корректно закрыт.
|
||||||
|
|
||||||
|
При `InterruptedException` необходимо восстановить статус прерывания текущего потока:
|
||||||
|
|
||||||
|
```java
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Использование `MyList`
|
||||||
|
|
||||||
|
Сторонние контейнеры **не допускаются**.
|
||||||
|
|
||||||
|
Запрещено использовать:
|
||||||
|
|
||||||
|
```text
|
||||||
|
ArrayList
|
||||||
|
LinkedList
|
||||||
|
Vector
|
||||||
|
CopyOnWriteArrayList
|
||||||
|
ConcurrentLinkedQueue
|
||||||
|
и другие стандартные контейнеры
|
||||||
|
```
|
||||||
|
|
||||||
|
как хранилище промежуточных или итоговых результатов.
|
||||||
|
|
||||||
|
Для результатов каждого потока использовать:
|
||||||
|
|
||||||
|
```java
|
||||||
|
MyList<Integer>
|
||||||
|
```
|
||||||
|
|
||||||
|
Итог преобразовать в:
|
||||||
|
|
||||||
|
```java
|
||||||
|
int[]
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Требуется ли изменять `MyList`
|
||||||
|
|
||||||
|
Текущего интерфейса **достаточно для реализации задачи**:
|
||||||
|
|
||||||
|
```java
|
||||||
|
public interface MyList<T> {
|
||||||
|
|
||||||
|
void add(T element);
|
||||||
|
|
||||||
|
T get(int index);
|
||||||
|
|
||||||
|
T set(int index, T element);
|
||||||
|
|
||||||
|
T remove(int index);
|
||||||
|
|
||||||
|
int size();
|
||||||
|
|
||||||
|
boolean isEmpty();
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Дополнительные методы добавлять **не требуется**.
|
||||||
|
|
||||||
|
Для задачи достаточно:
|
||||||
|
|
||||||
|
```java
|
||||||
|
size()
|
||||||
|
get(index)
|
||||||
|
add(index)
|
||||||
|
```
|
||||||
|
|
||||||
|
Поскольку поиск выполняется только на чтение, а результаты каждого потока добавляются в его собственный `MyList<Integer>`, существующего интерфейса достаточно.
|
||||||
|
|
||||||
|
### Важно
|
||||||
|
|
||||||
|
Метод:
|
||||||
|
|
||||||
|
```java
|
||||||
|
void add(T element);
|
||||||
|
```
|
||||||
|
|
||||||
|
должен добавлять элемент в конец коллекции.
|
||||||
|
|
||||||
|
Это позволяет каждому потоку формировать свои индексы в порядке их обнаружения.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Использование `Student`
|
||||||
|
|
||||||
|
Изменения в `Student` **не требуются**.
|
||||||
|
|
||||||
|
Использовать существующий метод:
|
||||||
|
|
||||||
|
```java
|
||||||
|
equals(Object obj)
|
||||||
|
```
|
||||||
|
|
||||||
|
Сравнение выполнять:
|
||||||
|
|
||||||
|
```java
|
||||||
|
target.equals(students.get(index))
|
||||||
|
```
|
||||||
|
|
||||||
|
Не использовать:
|
||||||
|
|
||||||
|
```text
|
||||||
|
==
|
||||||
|
```
|
||||||
|
|
||||||
|
Не сравнивать объекты вручную по отдельным полям.
|
||||||
|
|
||||||
|
Не использовать `toString()` для определения совпадения.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# `StudentBuilder`
|
||||||
|
|
||||||
|
Изменения в `StudentBuilder` **не требуются**.
|
||||||
|
|
||||||
|
Он не участвует в алгоритме поиска.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Обработка входных данных
|
||||||
|
|
||||||
|
Если:
|
||||||
|
|
||||||
|
```java
|
||||||
|
students == null
|
||||||
|
```
|
||||||
|
|
||||||
|
выбросить:
|
||||||
|
|
||||||
|
```java
|
||||||
|
IllegalArgumentException
|
||||||
|
```
|
||||||
|
|
||||||
|
Если:
|
||||||
|
|
||||||
|
```java
|
||||||
|
target == null
|
||||||
|
```
|
||||||
|
|
||||||
|
выбросить:
|
||||||
|
|
||||||
|
```java
|
||||||
|
IllegalArgumentException
|
||||||
|
```
|
||||||
|
|
||||||
|
Для пустой коллекции вернуть:
|
||||||
|
|
||||||
|
```java
|
||||||
|
new int[0]
|
||||||
|
```
|
||||||
|
|
||||||
|
Без создания рабочих потоков.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Обобщённый алгоритм реализации
|
||||||
|
|
||||||
|
```text
|
||||||
|
1. Проверить students и target.
|
||||||
|
2. Получить размер коллекции.
|
||||||
|
3. Если размер равен 0 → вернуть [].
|
||||||
|
4. Вычислить количество потоков.
|
||||||
|
5. Разделить индексы коллекции на непересекающиеся диапазоны.
|
||||||
|
6. Создать Callable для каждого диапазона.
|
||||||
|
7. Запустить задачи через ExecutorService.
|
||||||
|
8. Каждый поток:
|
||||||
|
- получает fromIndex и toIndex;
|
||||||
|
- проходит свой диапазон;
|
||||||
|
- сравнивает Student через equals();
|
||||||
|
- записывает найденные индексы в собственный MyList<Integer>.
|
||||||
|
9. Основной поток получает результаты через Future.
|
||||||
|
10. Объединяет MyList<Integer> в порядке диапазонов.
|
||||||
|
11. Преобразует результат в int[].
|
||||||
|
12. Возвращает массив.
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Примеры тестов
|
||||||
|
|
||||||
|
## Одно совпадение
|
||||||
|
|
||||||
|
```text
|
||||||
|
[A, B, C, D]
|
||||||
|
```
|
||||||
|
|
||||||
|
Искомый:
|
||||||
|
|
||||||
|
```text
|
||||||
|
C
|
||||||
|
```
|
||||||
|
|
||||||
|
Ожидается:
|
||||||
|
|
||||||
|
```text
|
||||||
|
[2]
|
||||||
|
```
|
||||||
|
|
||||||
|
## Несколько совпадений
|
||||||
|
|
||||||
|
```text
|
||||||
|
[A, B, A, C, A]
|
||||||
|
```
|
||||||
|
|
||||||
|
Ожидается:
|
||||||
|
|
||||||
|
```text
|
||||||
|
[0, 2, 4]
|
||||||
|
```
|
||||||
|
|
||||||
|
## Нет совпадений
|
||||||
|
|
||||||
|
```text
|
||||||
|
[A, B, C]
|
||||||
|
```
|
||||||
|
|
||||||
|
Искомый:
|
||||||
|
|
||||||
|
```text
|
||||||
|
D
|
||||||
|
```
|
||||||
|
|
||||||
|
Ожидается:
|
||||||
|
|
||||||
|
```text
|
||||||
|
[]
|
||||||
|
```
|
||||||
|
|
||||||
|
## Все элементы совпадают
|
||||||
|
|
||||||
|
```text
|
||||||
|
[A, A, A, A]
|
||||||
|
```
|
||||||
|
|
||||||
|
Ожидается:
|
||||||
|
|
||||||
|
```text
|
||||||
|
[0, 1, 2, 3]
|
||||||
|
```
|
||||||
|
|
||||||
|
## Пустая коллекция
|
||||||
|
|
||||||
|
Ожидается:
|
||||||
|
|
||||||
|
```text
|
||||||
|
[]
|
||||||
|
```
|
||||||
|
|
||||||
|
## Один элемент
|
||||||
|
|
||||||
|
Проверить:
|
||||||
|
|
||||||
|
```text
|
||||||
|
[A] + A → [0]
|
||||||
|
[A] + B → []
|
||||||
|
```
|
||||||
|
|
||||||
|
## Проверка границ диапазонов потоков
|
||||||
|
|
||||||
|
Проверить коллекции размеров:
|
||||||
|
|
||||||
|
```text
|
||||||
|
1
|
||||||
|
2
|
||||||
|
3
|
||||||
|
4
|
||||||
|
10
|
||||||
|
100
|
||||||
|
1000
|
||||||
|
```
|
||||||
|
|
||||||
|
Особое внимание уделить совпадениям:
|
||||||
|
|
||||||
|
```text
|
||||||
|
на последнем элементе одного диапазона;
|
||||||
|
на первом элементе следующего диапазона.
|
||||||
|
```
|
||||||
|
|
||||||
|
Каждый индекс должен быть найден ровно один раз.
|
||||||
|
|
||||||
|
## Проверка порядка результата
|
||||||
|
|
||||||
|
Создать совпадения в разных диапазонах.
|
||||||
|
|
||||||
|
Проверить, что результат имеет вид:
|
||||||
|
|
||||||
|
```text
|
||||||
|
[0, 3, 7, 12, 25, 49, 100]
|
||||||
|
```
|
||||||
|
|
||||||
|
и находится в порядке возрастания без использования дополнительной сортировки.
|
||||||
|
|
||||||
|
## Проверка `Student.equals()`
|
||||||
|
|
||||||
|
Создать два разных экземпляра `Student` с одинаковыми значениями полей.
|
||||||
|
|
||||||
|
Один поместить в коллекцию, второй передать как `target`.
|
||||||
|
|
||||||
|
Проверить, что индекс считается совпадением согласно `equals()`.
|
||||||
|
|
||||||
|
## Проверка неизменности коллекции
|
||||||
|
|
||||||
|
После выполнения метода проверить:
|
||||||
|
|
||||||
|
- размер `MyList`;
|
||||||
|
- порядок элементов;
|
||||||
|
- значения всех элементов.
|
||||||
|
|
||||||
|
## Проверка отсутствия вывода
|
||||||
|
|
||||||
|
Перехватить стандартный вывод и убедиться, что выполнение `findOccurrences()` не выводит сообщений в консоль.
|
||||||
|
|
||||||
|
## Большая коллекция
|
||||||
|
|
||||||
|
Создать коллекцию, например из `100000` студентов, с заранее известными индексами совпадений.
|
||||||
|
|
||||||
|
Проверить:
|
||||||
|
|
||||||
|
- все ожидаемые индексы найдены;
|
||||||
|
- лишних индексов нет;
|
||||||
|
- индексы идут по возрастанию;
|
||||||
|
- итоговый массив имеет правильный размер.
|
||||||
|
|
||||||
|
## Git
|
||||||
|
|
||||||
|
Название ветки:
|
||||||
|
|
||||||
|
```text
|
||||||
|
feature/student-occurrence-index-finder
|
||||||
|
```
|
||||||
|
|
||||||
|
## Результат
|
||||||
|
|
||||||
|
Необходимо реализовать:
|
||||||
|
|
||||||
|
```text
|
||||||
|
StudentOccurrenceIndexFinder.java
|
||||||
|
```
|
||||||
|
|
||||||
|
Существующие интерфейсы и классы:
|
||||||
|
|
||||||
|
```text
|
||||||
|
MyList.java
|
||||||
|
Student.java
|
||||||
|
StudentBuilder.java
|
||||||
|
```
|
||||||
|
|
||||||
|
изменять не требуется.
|
||||||
@@ -0,0 +1,326 @@
|
|||||||
|
# Техническое задание: обновление `Student`
|
||||||
|
|
||||||
|
## Задача
|
||||||
|
|
||||||
|
Обновить класс `Student` и связанный с ним код в соответствии с новой моделью данных и требованиями к неизменяемости объекта.
|
||||||
|
|
||||||
|
Текущая реализация содержит:
|
||||||
|
|
||||||
|
```java
|
||||||
|
private final String groupNumber;
|
||||||
|
private final double averageGrade;
|
||||||
|
private final String recordBookNumber;
|
||||||
|
```
|
||||||
|
|
||||||
|
Необходимо изменить тип `recordBookNumber` и добавить методы создания изменённых копий `Student`.
|
||||||
|
|
||||||
|
## Изменения в `Student`
|
||||||
|
|
||||||
|
### 1. Изменить тип `recordBookNumber`
|
||||||
|
|
||||||
|
Поле:
|
||||||
|
|
||||||
|
```java
|
||||||
|
private final String recordBookNumber;
|
||||||
|
```
|
||||||
|
|
||||||
|
заменить на:
|
||||||
|
|
||||||
|
```java
|
||||||
|
private final int recordBookNumber;
|
||||||
|
```
|
||||||
|
|
||||||
|
Метод:
|
||||||
|
|
||||||
|
```java
|
||||||
|
getRecordBookNumber()
|
||||||
|
```
|
||||||
|
|
||||||
|
должен возвращать `int`.
|
||||||
|
|
||||||
|
Все проверки, `equals()`, `hashCode()` и `toString()` должны быть адаптированы под новый тип.
|
||||||
|
|
||||||
|
### 2. Добавить бизнес-правило для `groupNumber`
|
||||||
|
|
||||||
|
`groupNumber` должен соответствовать строго следующему формату:
|
||||||
|
|
||||||
|
```text
|
||||||
|
<Заглавная буква латинского алфавита><2 цифры>
|
||||||
|
```
|
||||||
|
|
||||||
|
Регулярное выражение:
|
||||||
|
|
||||||
|
```text
|
||||||
|
^[A-Z][0-9]{2}$
|
||||||
|
```
|
||||||
|
|
||||||
|
Допустимые значения:
|
||||||
|
|
||||||
|
```text
|
||||||
|
A12
|
||||||
|
B32
|
||||||
|
Z00
|
||||||
|
```
|
||||||
|
|
||||||
|
Недопустимые значения:
|
||||||
|
|
||||||
|
```text
|
||||||
|
а12
|
||||||
|
a12
|
||||||
|
A1
|
||||||
|
A123
|
||||||
|
AA1
|
||||||
|
а1А
|
||||||
|
аа!!а123
|
||||||
|
23фи!12
|
||||||
|
№№###*()
|
||||||
|
```
|
||||||
|
|
||||||
|
Проверка должна выполняться при создании `Student`.
|
||||||
|
|
||||||
|
`Student` не может существовать с некорректным `groupNumber`.
|
||||||
|
|
||||||
|
### 3. Проверить `averageGrade`
|
||||||
|
|
||||||
|
Сохранить правило:
|
||||||
|
|
||||||
|
```text
|
||||||
|
0.0 <= averageGrade <= 5.0
|
||||||
|
```
|
||||||
|
|
||||||
|
Значения `0.0` и `5.0` допустимы.
|
||||||
|
|
||||||
|
### 4. Проверить `recordBookNumber`
|
||||||
|
|
||||||
|
После преобразования в `int` установить бизнес-правило:
|
||||||
|
|
||||||
|
```text
|
||||||
|
recordBookNumber > 0
|
||||||
|
```
|
||||||
|
|
||||||
|
Значение `0` и отрицательные значения считаются недопустимыми.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Методы `with...()`
|
||||||
|
|
||||||
|
Добавить методы, возвращающие **новый объект `Student`**, не изменяя текущий объект.
|
||||||
|
|
||||||
|
Необходимо реализовать:
|
||||||
|
|
||||||
|
```java
|
||||||
|
withGroupNumber(String groupNumber)
|
||||||
|
withAverageGrade(double averageGrade)
|
||||||
|
withRecordBookNumber(int recordBookNumber)
|
||||||
|
```
|
||||||
|
|
||||||
|
Каждый метод должен:
|
||||||
|
|
||||||
|
1. Сохранять исходный объект без изменений.
|
||||||
|
2. Изменять только указанное поле в новом объекте.
|
||||||
|
3. Сохранять остальные поля исходного объекта.
|
||||||
|
4. Повторно применять все правила валидации.
|
||||||
|
5. Возвращать новый валидный объект `Student`.
|
||||||
|
|
||||||
|
Пример:
|
||||||
|
|
||||||
|
```java
|
||||||
|
Student updated = student.withAverageGrade(4.8);
|
||||||
|
```
|
||||||
|
|
||||||
|
После выполнения:
|
||||||
|
|
||||||
|
```text
|
||||||
|
student.getAverageGrade() == прежнее значение
|
||||||
|
updated.getAverageGrade() == 4.8
|
||||||
|
```
|
||||||
|
|
||||||
|
Если новое значение невалидно, метод должен выбросить исключение, а исходный объект не должен измениться.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# `StudentBuilder`
|
||||||
|
|
||||||
|
Обновить существующий `StudentBuilder` в соответствии с новой моделью `Student`.
|
||||||
|
|
||||||
|
Методы должны соответствовать типам:
|
||||||
|
|
||||||
|
```java
|
||||||
|
setGroupNumber(String groupNumber)
|
||||||
|
setAverageGrade(double averageGrade)
|
||||||
|
setRecordBookNumber(int recordBookNumber)
|
||||||
|
```
|
||||||
|
|
||||||
|
`build()` должен создавать только валидный `Student`.
|
||||||
|
|
||||||
|
Все новые правила валидации должны применяться при создании объекта через `StudentBuilder`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Обновление зависимого кода
|
||||||
|
|
||||||
|
Найти и обновить весь код, использующий `Student` и изменяемые методы или типы.
|
||||||
|
|
||||||
|
В частности, обновить:
|
||||||
|
|
||||||
|
- `Comparator<Student>`;
|
||||||
|
- сортировки;
|
||||||
|
- источники данных;
|
||||||
|
- тестовые классы;
|
||||||
|
- создание `Student`;
|
||||||
|
- `with...()`-операции;
|
||||||
|
- чтение и запись данных, если они используют `recordBookNumber`.
|
||||||
|
|
||||||
|
Например, существующий:
|
||||||
|
|
||||||
|
```java
|
||||||
|
implements Comparator<Student>
|
||||||
|
```
|
||||||
|
|
||||||
|
должен использовать актуальный метод:
|
||||||
|
|
||||||
|
```java
|
||||||
|
getAverageGrade()
|
||||||
|
```
|
||||||
|
|
||||||
|
вместо ошибочного `getAvarageGrade()`.
|
||||||
|
|
||||||
|
Компаратор `recordBookNumber` должен сравнивать значения как числа.
|
||||||
|
|
||||||
|
Например:
|
||||||
|
|
||||||
|
```java
|
||||||
|
Integer.compare(
|
||||||
|
s1.getRecordBookNumber(),
|
||||||
|
s2.getRecordBookNumber()
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
Все места, где `recordBookNumber` рассматривался как `String`, необходимо адаптировать к `int`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Тесты
|
||||||
|
|
||||||
|
Обновить существующий `StudentTest` с учётом новой модели.
|
||||||
|
|
||||||
|
## Создание корректного `Student`
|
||||||
|
|
||||||
|
Проверить создание объекта со значениями:
|
||||||
|
|
||||||
|
```text
|
||||||
|
groupNumber = A12
|
||||||
|
averageGrade = 4.5
|
||||||
|
recordBookNumber = 12345
|
||||||
|
```
|
||||||
|
|
||||||
|
Проверить корректность всех геттеров.
|
||||||
|
|
||||||
|
## Проверка `groupNumber`
|
||||||
|
|
||||||
|
Проверить успешное создание для:
|
||||||
|
|
||||||
|
```text
|
||||||
|
A12
|
||||||
|
B32
|
||||||
|
Z00
|
||||||
|
```
|
||||||
|
|
||||||
|
Проверить исключение для:
|
||||||
|
|
||||||
|
```text
|
||||||
|
a12
|
||||||
|
A1
|
||||||
|
A123
|
||||||
|
AA1
|
||||||
|
а1А
|
||||||
|
аа!!а123
|
||||||
|
23фи!12
|
||||||
|
№№###*()
|
||||||
|
```
|
||||||
|
|
||||||
|
## Проверка `averageGrade`
|
||||||
|
|
||||||
|
Проверить:
|
||||||
|
|
||||||
|
```text
|
||||||
|
0.0 — допустимо
|
||||||
|
5.0 — допустимо
|
||||||
|
-1.0 — недопустимо
|
||||||
|
6.0 — недопустимо
|
||||||
|
```
|
||||||
|
|
||||||
|
## Проверка `recordBookNumber`
|
||||||
|
|
||||||
|
Проверить:
|
||||||
|
|
||||||
|
```text
|
||||||
|
1 — допустимо
|
||||||
|
12345 — допустимо
|
||||||
|
0 — недопустимо
|
||||||
|
-1 — недопустимо
|
||||||
|
```
|
||||||
|
|
||||||
|
## Тесты `with...()`
|
||||||
|
|
||||||
|
### `withGroupNumber()`
|
||||||
|
|
||||||
|
Проверить, что:
|
||||||
|
|
||||||
|
- возвращается новый объект;
|
||||||
|
- `groupNumber` изменён;
|
||||||
|
- `averageGrade` не изменился;
|
||||||
|
- `recordBookNumber` не изменился;
|
||||||
|
- исходный `Student` остался прежним.
|
||||||
|
|
||||||
|
### `withAverageGrade()`
|
||||||
|
|
||||||
|
Проверить те же свойства для `averageGrade`.
|
||||||
|
|
||||||
|
### `withRecordBookNumber()`
|
||||||
|
|
||||||
|
Проверить те же свойства для `recordBookNumber`.
|
||||||
|
|
||||||
|
### `with...()` с невалидным значением
|
||||||
|
|
||||||
|
Для каждого метода передать некорректное значение.
|
||||||
|
|
||||||
|
Проверить:
|
||||||
|
|
||||||
|
- выбрасывается `IllegalArgumentException`;
|
||||||
|
- исходный объект не изменяется;
|
||||||
|
- не создаётся невалидный `Student`.
|
||||||
|
|
||||||
|
## `equals()` и `hashCode()`
|
||||||
|
|
||||||
|
Обновить существующие тесты с учётом `int recordBookNumber`.
|
||||||
|
|
||||||
|
Проверить:
|
||||||
|
|
||||||
|
- одинаковые студенты равны;
|
||||||
|
- одинаковые студенты имеют одинаковый `hashCode`;
|
||||||
|
- изменение любого поля создаёт отличающийся объект.
|
||||||
|
|
||||||
|
## Результат
|
||||||
|
|
||||||
|
Необходимо обновить:
|
||||||
|
|
||||||
|
```text
|
||||||
|
Student.java
|
||||||
|
StudentBuilder.java
|
||||||
|
StudentTest.java
|
||||||
|
```
|
||||||
|
|
||||||
|
а также **все классы проекта, зависящие от изменённого интерфейса `Student`**, включая компараторы и другие реализации, использующие `recordBookNumber`, `groupNumber`, `averageGrade` или их геттеры.
|
||||||
|
|
||||||
|
### Основной принцип
|
||||||
|
|
||||||
|
`Student` должен оставаться единственной точкой истины для валидности данных:
|
||||||
|
|
||||||
|
```text
|
||||||
|
StudentBuilder ──┐
|
||||||
|
├──> Student
|
||||||
|
with...() ───────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
Ни `Builder`, ни `with...()`, ни внешние классы не должны позволять создать невалидный объект.
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
# Общее техническое задание: тестирование
|
||||||
|
|
||||||
|
## Задача
|
||||||
|
|
||||||
|
Для всех реализованных классов написать автоматические тесты с использованием **JUnit**.
|
||||||
|
|
||||||
|
## Требования
|
||||||
|
|
||||||
|
1. Для каждого основного класса проекта должны быть написаны тесты.
|
||||||
|
|
||||||
|
2. Проверить:
|
||||||
|
|
||||||
|
- корректное выполнение основных методов;
|
||||||
|
- корректную обработку некорректных данных;
|
||||||
|
- граничные случаи;
|
||||||
|
- ожидаемые исключения.
|
||||||
|
|
||||||
|
3. Для `Student` проверить:
|
||||||
|
|
||||||
|
- создание корректного объекта;
|
||||||
|
- невозможность создания объекта с некорректными данными;
|
||||||
|
- работу `Builder`;
|
||||||
|
- `equals()` и `hashCode()`.
|
||||||
|
|
||||||
|
4. Для `MyLinkedList` проверить:
|
||||||
|
|
||||||
|
- `add()`;
|
||||||
|
- `get()`;
|
||||||
|
- `set()`;
|
||||||
|
- `remove()`;
|
||||||
|
- `size()`;
|
||||||
|
- `isEmpty()`;
|
||||||
|
- некорректные индексы.
|
||||||
|
|
||||||
|
5. Для `MergeSortStrategy` проверить:
|
||||||
|
|
||||||
|
- сортировку по каждому из трёх полей;
|
||||||
|
- уже отсортированную коллекцию;
|
||||||
|
- обратный порядок;
|
||||||
|
- коллекцию из одного элемента;
|
||||||
|
- пустую коллекцию.
|
||||||
|
|
||||||
|
6. Для каждого компаратора проверить правильное сравнение объектов `Student`.
|
||||||
|
|
||||||
|
7. Тесты должны быть независимыми друг от друга и воспроизводимыми.
|
||||||
|
|
||||||
|
8. Необходимо использовать JUnit и стандартные средства проверки результатов.
|
||||||
|
|
||||||
|
## Результат
|
||||||
|
|
||||||
|
Тесты размещать отдельно от основного кода, например:
|
||||||
|
|
||||||
|
```text
|
||||||
|
src/
|
||||||
|
├── main/
|
||||||
|
│ └── java/
|
||||||
|
└── test/
|
||||||
|
└── java/
|
||||||
|
├── StudentTest.java
|
||||||
|
├── MyLinkedListTest.java
|
||||||
|
├── MergeSortStrategyTest.java
|
||||||
|
├── StudentGroupComparatorTest.java
|
||||||
|
├── StudentAverageGradeComparatorTest.java
|
||||||
|
└── StudentRecordBookComparatorTest.java
|
||||||
|
```
|
||||||
|
|
||||||
|
Все тесты должны успешно проходить перед слиянием ветки в `main`.
|
||||||
Reference in New Issue
Block a user