From f07d181cf7d7a3a454bebcf04e9ccefa86ccd72d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D0=BE=D0=BB=D0=BE=D0=B4=D0=B8=D0=BD=20=D0=93=D0=B5?= =?UTF-8?q?=D1=80=D0=BC=D0=B0=D0=BD?= Date: Fri, 21 Aug 2026 19:02:34 +0400 Subject: [PATCH 01/14] docs: add task specifications for project components --- docs/tasks/CustomList.md | 44 +++++++++++++++++++++++++++++++++++ docs/tasks/DataSource.md | 44 +++++++++++++++++++++++++++++++++++ docs/tasks/Sorting.md | 50 ++++++++++++++++++++++++++++++++++++++++ docs/tasks/Student.md | 46 ++++++++++++++++++++++++++++++++++++ 4 files changed, 184 insertions(+) create mode 100644 docs/tasks/CustomList.md create mode 100644 docs/tasks/DataSource.md create mode 100644 docs/tasks/Sorting.md create mode 100644 docs/tasks/Student.md diff --git a/docs/tasks/CustomList.md b/docs/tasks/CustomList.md new file mode 100644 index 0000000..e95066e --- /dev/null +++ b/docs/tasks/CustomList.md @@ -0,0 +1,44 @@ +# Техническое задание: кастомный контейнер List + +## Задача + +Реализовать собственный контейнер для хранения объектов, не используя готовые реализации `List`, `ArrayList` и `LinkedList`. + +## Требования + +1. Создать интерфейс `MyList`. + +2. В интерфейсе определить основные операции: + + - `add(...)`; + - `get(...)`; + - `set(...)`; + - `remove(...)`; + - `size()`; + - `isEmpty()`. + +3. Создать класс `MyLinkedList`, реализующий `MyList`. + +4. Реализовать контейнер на основе собственных узлов `Node`. + +5. Контейнер должен поддерживать: + + - добавление элемента; + - получение элемента по индексу; + - замену элемента по индексу; + - удаление элемента по индексу; + - получение количества элементов; + - проверку на пустоту. + +6. Обработать некорректные индексы с помощью исключений. + +7. Контейнер не должен зависеть от класса `Student`, сортировки или пользовательского интерфейса. + +## Результат + +Должны быть реализованы: + +```text +MyList.java +MyLinkedList.java +``` diff --git a/docs/tasks/DataSource.md b/docs/tasks/DataSource.md new file mode 100644 index 0000000..3c42760 --- /dev/null +++ b/docs/tasks/DataSource.md @@ -0,0 +1,44 @@ +# Техническое задание: заполнение коллекции + +## Задача + +Реализовать разные способы заполнения `MyList` данными. + +## Требования + +1. Создать интерфейс `DataSource` с методом: + +```text +fill(int size) +``` + +2. Реализовать три способа заполнения: + + - `ManualDataSource` — ручной ввод; + - `RandomDataSource` — случайная генерация; + - `FileDataSource` — чтение из файла. + +3. Результатом работы каждого источника должна быть коллекция `MyList`. + +4. Для создания объектов `Student` использовать `StudentBuilder`. + +5. Все данные должны проходить валидацию средствами `Student`. + +6. При ручном вводе некорректные данные не должны добавляться в коллекцию. + +7. При чтении из файла некорректные строки должны обрабатываться без создания невалидных объектов `Student`. + +8. Размер коллекции должен задаваться пользователем и соблюдаться при заполнении. + +9. Не использовать стандартные коллекции `List`, `ArrayList`, `LinkedList` для хранения результата. + +## Результат + +```text +DataSource.java +ManualDataSource.java +RandomDataSource.java +FileDataSource.java +``` + +Классы не должны содержать логику сортировки или пользовательского меню. diff --git a/docs/tasks/Sorting.md b/docs/tasks/Sorting.md new file mode 100644 index 0000000..65bbffa --- /dev/null +++ b/docs/tasks/Sorting.md @@ -0,0 +1,50 @@ +# Техническое задание: сортировка + +## Задача + +Реализовать паттерн Strategy и собственный алгоритм сортировки объектов `Student`. + +## Требования + +1. Создать интерфейс `SortStrategy` с методом: + +```text +sort(MyList list, Comparator comparator) +``` + +1. Создать класс `BubbleSortStrategy`, реализующий `SortStrategy`. + +2. Алгоритм сортировки реализовать самостоятельно. Готовые методы сортировки Java не использовать. + +3. Сортировка должна работать с `MyList`, а не со стандартными коллекциями Java. + +4. Сортировка должна использовать переданный `Comparator`. + +5. Реализовать возможность сортировки `Student` по каждому из трёх полей: + + - `groupNumber`; + - `averageGrade`; + - `recordBookNumber`. + +6. Для каждого поля создать отдельный компаратор: + + - `StudentGroupComparator`; + - `StudentAverageGradeComparator`; + - `StudentRecordBookComparator`. + +7. Алгоритм сортировки не должен зависеть от класса `Student`. + +## Результат + +Должны быть реализованы: + +```text +SortStrategy.java +BubbleSortStrategy.java + +StudentGroupComparator.java +StudentAverageGradeComparator.java +StudentRecordBookComparator.java +``` + +Классы должны работать с `MyList` и не использовать готовые реализации сортировки. diff --git a/docs/tasks/Student.md b/docs/tasks/Student.md new file mode 100644 index 0000000..2e17172 --- /dev/null +++ b/docs/tasks/Student.md @@ -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 +``` + +Классы не должны зависеть от сортировки, коллекции, меню и ввода данных. From 1d3adceed083975bcda21110e92857d1a1fcc565 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D0=BE=D0=BB=D0=BE=D0=B4=D0=B8=D0=BD=20=D0=93=D0=B5?= =?UTF-8?q?=D1=80=D0=BC=D0=B0=D0=BD?= Date: Fri, 21 Aug 2026 20:08:28 +0400 Subject: [PATCH 02/14] test: add JUnit test requirements --- docs/tasks/Testing.md | 67 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 docs/tasks/Testing.md diff --git a/docs/tasks/Testing.md b/docs/tasks/Testing.md new file mode 100644 index 0000000..26f898c --- /dev/null +++ b/docs/tasks/Testing.md @@ -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`. From c8d2683c5d5995d99e1ec47a6fbcfe7381b26de1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D0=BE=D0=BB=D0=BE=D0=B4=D0=B8=D0=BD=20=D0=93=D0=B5?= =?UTF-8?q?=D1=80=D0=BC=D0=B0=D0=BD?= Date: Fri, 21 Aug 2026 20:09:43 +0400 Subject: [PATCH 03/14] docs: update sorting task to merge sort --- docs/tasks/Sorting.md | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/docs/tasks/Sorting.md b/docs/tasks/Sorting.md index 65bbffa..3fc39b0 100644 --- a/docs/tasks/Sorting.md +++ b/docs/tasks/Sorting.md @@ -1,8 +1,8 @@ -# Техническое задание: сортировка +# Техническое задание: сортировка слиянием ## Задача -Реализовать паттерн Strategy и собственный алгоритм сортировки объектов `Student`. +Реализовать паттерн Strategy и собственный алгоритм сортировки слиянием объектов `Student`. ## Требования @@ -12,39 +12,37 @@ sort(MyList list, Comparator comparator) ``` -1. Создать класс `BubbleSortStrategy`, реализующий `SortStrategy`. +1. Создать класс `MergeSortStrategy`, реализующий `SortStrategy`. -2. Алгоритм сортировки реализовать самостоятельно. Готовые методы сортировки Java не использовать. +2. Реализовать алгоритм сортировки слиянием самостоятельно. -3. Сортировка должна работать с `MyList`, а не со стандартными коллекциями Java. +3. Готовые методы сортировки Java не использовать. -4. Сортировка должна использовать переданный `Comparator`. +4. Сортировка должна работать с `MyList`. -5. Реализовать возможность сортировки `Student` по каждому из трёх полей: +5. Сортировка должна использовать переданный `Comparator`. + +6. Реализовать сортировку `Student` по каждому из трёх полей: - `groupNumber`; - `averageGrade`; - `recordBookNumber`. -6. Для каждого поля создать отдельный компаратор: +7. Создать отдельные компараторы: - `StudentGroupComparator`; - `StudentAverageGradeComparator`; - `StudentRecordBookComparator`. -7. Алгоритм сортировки не должен зависеть от класса `Student`. +8. `MergeSortStrategy` не должен зависеть от класса `Student`. ## Результат -Должны быть реализованы: - ```text SortStrategy.java -BubbleSortStrategy.java +MergeSortStrategy.java StudentGroupComparator.java StudentAverageGradeComparator.java StudentRecordBookComparator.java ``` - -Классы должны работать с `MyList` и не использовать готовые реализации сортировки. From 014442453d7c169d7de7e3caa5de8fd93cc7f71d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D0=BE=D0=BB=D0=BE=D0=B4=D0=B8=D0=BD=20=D0=93=D0=B5?= =?UTF-8?q?=D1=80=D0=BC=D0=B0=D0=BD?= Date: Tue, 25 Aug 2026 18:54:48 +0400 Subject: [PATCH 04/14] docs: add even-only merge sort task --- docs/tasks/EvenOnlyMergeSort.md | 156 ++++++++++++++++++++++++++++++++ 1 file changed, 156 insertions(+) create mode 100644 docs/tasks/EvenOnlyMergeSort.md diff --git a/docs/tasks/EvenOnlyMergeSort.md b/docs/tasks/EvenOnlyMergeSort.md new file mode 100644 index 0000000..acfd270 --- /dev/null +++ b/docs/tasks/EvenOnlyMergeSort.md @@ -0,0 +1,156 @@ +# Техническое задание: сортировка по чётным значениям + +## Задача + +Дополнить существующий алгоритм сортировки слиянием специальной сортировкой, при которой элементы с чётным значением выбранного числового поля сортируются, а элементы с нечётным значением сохраняют свои исходные позиции. + +## Контракты + +1. Стратегия должна реализовывать существующий интерфейс `SortStrategy`. + +2. Стратегия должна работать с `MyList`. + +3. Алгоритм должен быть основан на сортировке слиянием. + +4. Готовые методы сортировки Java использовать запрещено. + +5. Стратегия не должна зависеть непосредственно от класса `Student`. + +6. Числовое поле должно передаваться в стратегию извне в виде функции получения значения: + +```java +ToIntFunction +``` + +1. Функция получения числового значения должна: + + - возвращать значение выбранного поля; + - использоваться для проверки чётности; + - не содержать дополнительной логики сортировки. + +2. Стратегия не должна самостоятельно выбирать поле через: + + - `if`; + - `switch`; + - проверку типа `Student`; + - прямое обращение к конкретным полям `Student`. + +3. Для `Student` доступны функции получения числовых полей: + +```java +Student::getGroupNumber +Student::getRecordBookNumber +``` + + 1. Поле `averageGrade` не используется для определения чётности, если оно имеет нецелочисленный тип. + +## Правила сортировки + +1. Элементы с чётным значением выбранного поля участвуют в сортировке. + +2. Элементы с нечётным значением выбранного поля не перемещаются. + +3. Чётные элементы должны занимать только те позиции, которые до сортировки принадлежали чётным элементам. + +4. Чётные элементы сортируются в натуральном порядке выбранного числового поля. + +5. Нечётные элементы должны остаться на своих исходных позициях. + +6. Количество элементов коллекции не должно изменяться. + +### Пример + +Исходная коллекция: + +```text +[3, 8, 5, 2, 4, 7] +``` + +Результат: + +```text +[3, 2, 5, 4, 8, 7] +``` + +Нечётные значения `3`, `5`, `7` остались на исходных позициях. + +Чётные значения `8`, `2`, `4` были отсортированы по возрастанию и заняли позиции, которые изначально принадлежали чётным элементам. + +## Примеры тестов + +### Тест сортировки по номеру группы + +Использовать `Student::getGroupNumber`: + +```java +MyList students = ...; + +SortStrategy strategy = + new EvenOnlyMergeSortStrategy<>(Student::getGroupNumber); + +strategy.sort(students, new StudentGroupComparator()); +``` + +Проверить, что: + +- студенты с нечётным номером группы остались на исходных позициях; +- студенты с чётным номером группы отсортированы по возрастанию; +- количество элементов не изменилось. + +### Тест сортировки по номеру зачётной книжки + +Использовать `Student::getRecordBookNumber`. + +Проверить те же условия для номера зачётной книжки. + +### Тест без чётных элементов + +Исходные значения: + +```text +[1, 3, 5, 7] +``` + +Результат должен быть идентичен исходной коллекции. + +### Тест без нечётных элементов + +Исходные значения: + +```text +[8, 2, 6, 4] +``` + +Результат: + +```text +[2, 4, 6, 8] +``` + +### Тест с одним элементом + +Исходные значения: + +```text +[4] +``` + +Результат: + +```text +[4] +``` + +### Тест с пустой коллекцией + +Сортировка пустой коллекции не должна приводить к ошибке. + +## Результат + +Реализовать: + +```text +EvenOnlyMergeSortStrategy.java +``` + +Класс должен принимать `ToIntFunction` через конструктор и работать с существующими `MyList`, `SortStrategy` и компараторами. From 304f66986848f7be44aa78c3eb99b9fad2dbcc5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D0=BE=D0=BB=D0=BE=D0=B4=D0=B8=D0=BD=20=D0=93=D0=B5?= =?UTF-8?q?=D1=80=D0=BC=D0=B0=D0=BD?= Date: Tue, 25 Aug 2026 19:18:20 +0400 Subject: [PATCH 05/14] docs: improve data source task specification --- docs/tasks/DataSource.md | 393 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 374 insertions(+), 19 deletions(-) diff --git a/docs/tasks/DataSource.md b/docs/tasks/DataSource.md index 3c42760..79a6c14 100644 --- a/docs/tasks/DataSource.md +++ b/docs/tasks/DataSource.md @@ -1,38 +1,393 @@ -# Техническое задание: заполнение коллекции +# Техническое задание: источники данных ## Задача -Реализовать разные способы заполнения `MyList` данными. +Реализовать три независимых источника данных для формирования `MyList`: -## Требования +- `ManualDataSource`; +- `RandomDataSource`; +- `FileDataSource`. -1. Создать интерфейс `DataSource` с методом: +Все источники должны формировать только валидные объекты `Student`. -```text -fill(int size) +Класс `Student` содержит: + +```java +private final String groupNumber; +private final double averageGrade; +private final String recordBookNumber; ``` -2. Реализовать три способа заполнения: +Создание объектов `Student` выполнять через `StudentBuilder`. - - `ManualDataSource` — ручной ввод; - - `RandomDataSource` — случайная генерация; - - `FileDataSource` — чтение из файла. +--- -3. Результатом работы каждого источника должна быть коллекция `MyList`. +# Общий контракт -4. Для создания объектов `Student` использовать `StudentBuilder`. +Создать интерфейс `DataSource`: -5. Все данные должны проходить валидацию средствами `Student`. +```java +public interface DataSource { -6. При ручном вводе некорректные данные не должны добавляться в коллекцию. + MyList create(); +} +``` -7. При чтении из файла некорректные строки должны обрабатываться без создания невалидных объектов `Student`. +Источник данных не должен самостоятельно получать данные от пользователя. -8. Размер коллекции должен задаваться пользователем и соблюдаться при заполнении. +Все необходимые входные значения должны передаваться извне. -9. Не использовать стандартные коллекции `List`, `ArrayList`, `LinkedList` для хранения результата. +Результатом работы каждого источника является `MyList`. -## Результат +Стандартные `List`, `ArrayList` и `LinkedList` для хранения результата не использовать. + +--- + +# `ManualDataSource` + +## Назначение + +Постепенно формировать коллекцию `Student` на основе значений, передаваемых извне для каждого отдельного объекта. + +## Входные данные одного объекта + +Для создания одного `Student` передаются: + +```text +groupNumber +averageGrade +recordBookNumber +``` + +Также передаётся `index` — позиция в коллекции, на которой должен находиться создаваемый объект. + +Размер результирующей коллекции задаётся заранее. + +## Контракт + +`ManualDataSource` должен: + +1. Принимать требуемый размер коллекции. +2. Принимать значения одного `Student`: + + - `groupNumber`; + - `averageGrade`; + - `recordBookNumber`. + +3. Принимать индекс, на котором должен находиться этот объект. +4. Создавать новый `Student` через `StudentBuilder`. +5. Добавлять созданный объект в нужную позицию коллекции. +6. Позволять повторно передавать данные для существующего индекса, заменяя объект на новый. +7. Проверять допустимость индекса. +8. Не изменять уже созданный объект `Student`. +9. Позволять получить текущее состояние коллекции для просмотра. +10. После заполнения вернуть сформированную `MyList`. + +Рекомендуемый метод для передачи одного объекта: + +```java +setStudent( + int index, + String groupNumber, + double averageGrade, + String recordBookNumber +) +``` + +Таким образом, пользовательский код может последовательно передать данные для всех элементов: + +```java +dataSource.setStudent(0, "101", 4.5, "10001"); +dataSource.setStudent(1, "102", 3.8, "10002"); +dataSource.setStudent(2, "101", 4.9, "10003"); +``` + +После заполнения: + +```java +MyList students = dataSource.create(); +``` + +### Изменение объекта + +Для изменения объекта повторно вызывается `setStudent(...)` с нужным индексом: + +```java +dataSource.setStudent(1, "105", 4.2, "10025"); +``` + +Объект на позиции `1` заменяется новым валидным `Student`. + +### Размер коллекции + +Если задан: + +```text +size = 5 +``` + +то для успешного создания коллекции должны быть заданы данные для всех пяти позиций: + +```text +0 +1 +2 +3 +4 +``` + +Порядок заполнения не обязан совпадать с порядком индексов: + +```java +dataSource.setStudent(3, "103", 4.1, "10004"); +dataSource.setStudent(0, "101", 4.5, "10001"); +dataSource.setStudent(4, "102", 3.9, "10005"); +dataSource.setStudent(1, "101", 4.8, "10002"); +dataSource.setStudent(2, "104", 3.7, "10003"); +``` + +После заполнения коллекция должна содержать ровно пять объектов в соответствии с указанными индексами. + +Если хотя бы одна позиция не заполнена, `create()` должен сообщить об ошибке и не возвращать неполную коллекцию. + +--- + +# `RandomDataSource` + +## Назначение + +Создавать заданное количество `Student` со случайными значениями в диапазонах, заданных извне. + +## Входные данные + +Передаются: + +```text +size +``` + +и диапазоны для каждого поля: + +```text +groupNumber: + minimum + maximum + +averageGrade: + minimum + maximum + +recordBookNumber: + minimum + maximum +``` + +## Контракт + +`RandomDataSource` должен: + +1. Принять размер создаваемой коллекции. +2. Принять минимальное и максимальное значение каждого поля. +3. Проверить корректность каждого диапазона. +4. Сгенерировать значения в заданных пределах. +5. Создать каждый объект через `StudentBuilder`. +6. Поместить объекты в `MyList`. +7. Вернуть коллекцию заданного размера. + +Минимальное и максимальное значения включаются в диапазон генерации. + +`RandomDataSource` не должен самостоятельно получать или отображать параметры генерации. + +--- + +# `FileDataSource` + +## Назначение + +Создавать `MyList` на основании содержимого файла. + +## Входные данные + +Передаётся **абсолютный путь к файлу**. + +Относительный путь не является входным параметром `FileDataSource`. + +Формат одной записи: + +```text +groupNumber;averageGrade;recordBookNumber +``` + +Пример: + +```text +101;4.75;123456 +102;3.80;123457 +103;4.20;123458 +``` + +## Контракт + +`FileDataSource` должен: + +1. Получить абсолютный путь к файлу. +2. Открыть файл. +3. Последовательно обработать его записи. +4. Разделить каждую запись на три значения. +5. Создать `Student` через `StudentBuilder`. +6. Добавить только валидные объекты в `MyList`. +7. Пропустить некорректную запись и продолжить обработку остальных. +8. При невозможности открыть файл сообщить об ошибке. +9. Вернуть сформированную `MyList`. + +--- + +# Общие требования + +1. Все источники должны создавать `Student` только через `StudentBuilder`. +2. Источник данных не должен самостоятельно вводить данные. +3. Источник данных не должен зависеть от интерфейса пользователя. +4. Источник данных не должен содержать логику сортировки. +5. Результат хранить только в `MyList`. +6. Невалидный `Student` не должен попасть в коллекцию. +7. Проверка корректности самого объекта выполняется средствами `Student`. +8. Для ручного заполнения изменение существующего `Student` выполнять заменой объекта, а не изменением его полей. + +--- + +# Примеры тестов + +## `ManualDataSource` + +### Заполнение всей коллекции + +Создать источник размера `3`: + +```java +ManualDataSource dataSource = new ManualDataSource(3); + +dataSource.setStudent(0, "101", 4.5, "10001"); +dataSource.setStudent(1, "102", 3.8, "10002"); +dataSource.setStudent(2, "103", 4.2, "10003"); + +MyList result = dataSource.create(); +``` + +Проверить: + +```text +result.size() == 3 +``` + +и соответствие каждого объекта своему индексу. + +### Заполнение в произвольном порядке + +```java +dataSource.setStudent(2, "103", 4.2, "10003"); +dataSource.setStudent(0, "101", 4.5, "10001"); +dataSource.setStudent(1, "102", 3.8, "10002"); +``` + +Проверить правильное расположение объектов. + +### Изменение объекта + +После заполнения: + +```java +dataSource.setStudent(1, "105", 4.9, "10025"); +``` + +Проверить, что изменился только объект с индексом `1`. + +### Незаданный индекс + +Для коллекции размера `3` вызвать: + +```java +dataSource.setStudent(3, ...); +``` + +Проверить выброс исключения. + +### Неполное заполнение + +Заполнить только две позиции из трёх и вызвать `create()`. + +Проверить, что неполная коллекция не возвращается. + +--- + +## `RandomDataSource` + +### Корректная генерация + +Задать размер `100` и допустимые диапазоны для всех трёх полей. + +Проверить: + +```text +result.size() == 100 +``` + +и нахождение каждого значения внутри заданного диапазона. + +### Граничный диапазон + +Задать одинаковые минимальное и максимальное значения. + +Проверить, что все сгенерированные значения соответствуют этому значению. + +### Некорректный диапазон + +Задать: + +```text +minimum > maximum +``` + +Проверить выброс исключения. + +--- + +## `FileDataSource` + +### Корректный файл + +Для файла: + +```text +101;4.75;123456 +102;3.80;123457 +103;4.20;123458 +``` + +проверить получение трёх корректных объектов. + +### Файл с некорректной записью + +```text +101;4.75;123456 +invalid;abc;123457 +103;4.20;123458 +``` + +Проверить, что в коллекцию попадают только две корректные записи. + +### Пустой файл + +Проверить получение пустой коллекции. + +### Несуществующий файл + +Передать путь к несуществующему файлу и проверить корректную обработку ошибки. + +--- + +# Результат + +Реализовать: ```text DataSource.java @@ -41,4 +396,4 @@ RandomDataSource.java FileDataSource.java ``` -Классы не должны содержать логику сортировки или пользовательского меню. +Допускается создание вспомогательных классов для чтения файла и генерации случайных значений. From 6b10c04df25c8a11b93f67f95e301081f22d19d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D0=BE=D0=BB=D0=BE=D0=B4=D0=B8=D0=BD=20=D0=93=D0=B5?= =?UTF-8?q?=D1=80=D0=BC=D0=B0=D0=BD?= Date: Tue, 25 Aug 2026 22:08:40 +0400 Subject: [PATCH 06/14] docs: add stream collection task --- docs/tasks/StreamCollection.md | 327 +++++++++++++++++++++++++++++++++ 1 file changed, 327 insertions(+) create mode 100644 docs/tasks/StreamCollection.md diff --git a/docs/tasks/StreamCollection.md b/docs/tasks/StreamCollection.md new file mode 100644 index 0000000..e447a58 --- /dev/null +++ b/docs/tasks/StreamCollection.md @@ -0,0 +1,327 @@ +# Техническое задание: заполнение `MyList` посредством Stream API + +## Задача + +Реализовать механизм заполнения собственной коллекции `MyList` посредством Java Stream API без использования стандартных коллекций Java в качестве промежуточного или итогового хранилища. + +## Требования + +1. Итоговым результатом работы потока должна быть: + +```java +MyList +``` + +1. Для сбора элементов потока непосредственно в `MyList` реализовать собственный `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` + +Создать универсальный класс: + +```java +public class MyListCollector + implements Collector, MyList> { +} +``` + +Реализовать методы: + +```text +supplier() +accumulator() +combiner() +finisher() +characteristics() +``` + +Назначение: + +- `supplier()` — создать пустой `MyList`; +- `accumulator()` — добавить очередной элемент в `MyList`; +- `combiner()` — объединить две частично заполненные коллекции; +- `finisher()` — вернуть готовый `MyList`; +- `characteristics()` — определить характеристики сборщика. + +Коллектор должен работать с `Student` без специальной логики, связанной с его полями. + +--- + +## `MyCollectors` + +Создать вспомогательный класс: + +```java +public final class MyCollectors { + + public static Collector, MyList> toMyList() { + return new MyListCollector<>(); + } +} +``` + +Класс должен предоставлять удобный способ получения собственного сборщика. + +--- + +## Контракт использования + +Должна поддерживаться конструкция: + +```java +MyList students = Stream.of( + student1, + student2, + student3 +).collect(MyCollectors.toMyList()); +``` + +где `student1`, `student2`, `student3` — валидные объекты `Student`, предварительно созданные через `StudentBuilder`. + +Результатом должен быть именно `MyList`. + +--- + +## Пример `map` + +`map` должен использоваться для преобразования одного `Student` в новый `Student`. + +Исходный объект не изменяется. Для создания нового объекта использовать `with...()`. + +Одно из преобразуемых полей должно быть `String` и формироваться по заданному бизнес-правилу: + +```text +Заглавная латинская буква + двузначное число +``` + +Например: + +```text +A07 +B12 +Z35 +``` + +Пример: + +```java +MyList 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`, после чего он непосредственно собирается в `MyList`. + +Формат создаваемого `groupNumber` должен соответствовать: + +```text +^[A-Z][0-9]{2}$ +``` + +--- + +## Пример `filter` совместно с `map` + +Проверить совместную работу промежуточных операций: + +```java +MyList 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`. + +Реалистичный пример: + +```java +MyList 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 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 result = Stream.empty() + .collect(MyCollectors.toMyList()); +``` + +Проверить: + +```text +result != null +result.isEmpty() +``` + +### Проверка `map` + +Преобразовать несколько `Student` через `withGroupNumber()`. + +Проверить: + +- исходные объекты не изменились; +- созданные объекты отличаются от исходных; +- `groupNumber` соответствует формату `[A-Z][0-9]{2}`. + +### Проверка `filter` и `map` + +Проверить, что после `filter` и `map` в `MyList` попадают только необходимые студенты с корректно сформированным `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`, а не от конкретной реализации `MyLinkedList`. + +`MyCollectors` должен содержать только методы создания собственных сборщиков и не должен содержать бизнес-логику. From f504914bd0087b86539720501940ab2ea14b2925 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D0=BE=D0=BB=D0=BE=D0=B4=D0=B8=D0=BD=20=D0=93=D0=B5?= =?UTF-8?q?=D1=80=D0=BC=D0=B0=D0=BD?= Date: Tue, 25 Aug 2026 23:00:52 +0400 Subject: [PATCH 07/14] docs: refine EvenOnlyMergeSortStrategy specification --- docs/tasks/EvenOnlyMergeSort.md | 207 ++++++++++++++++++++------------ 1 file changed, 130 insertions(+), 77 deletions(-) diff --git a/docs/tasks/EvenOnlyMergeSort.md b/docs/tasks/EvenOnlyMergeSort.md index acfd270..ad5c4d3 100644 --- a/docs/tasks/EvenOnlyMergeSort.md +++ b/docs/tasks/EvenOnlyMergeSort.md @@ -2,148 +2,192 @@ ## Задача -Дополнить существующий алгоритм сортировки слиянием специальной сортировкой, при которой элементы с чётным значением выбранного числового поля сортируются, а элементы с нечётным значением сохраняют свои исходные позиции. +Дополнить существующую сортировку слиянием специальной стратегией `EvenOnlyMergeSortStrategy`. + +Стратегия должна сортировать только объекты, у которых выбранное числовое поле имеет чётное значение. Объекты с нечётным значением должны сохранять свои исходные позиции. ## Контракты -1. Стратегия должна реализовывать существующий интерфейс `SortStrategy`. +1. Стратегия должна реализовывать существующий интерфейс: -2. Стратегия должна работать с `MyList`. +```java +SortStrategy +``` -3. Алгоритм должен быть основан на сортировке слиянием. +1. Метод сортировки должен работать с: -4. Готовые методы сортировки Java использовать запрещено. +```java +MyList +``` -5. Стратегия не должна зависеть непосредственно от класса `Student`. +1. Для сортировки чётных элементов использовать алгоритм **Merge Sort**. -6. Числовое поле должно передаваться в стратегию извне в виде функции получения значения: +2. Готовые методы сортировки Java использовать запрещено. + +3. Стратегия не должна зависеть непосредственно от класса `Student`. + +4. Числовое поле передавать в стратегию извне через: ```java ToIntFunction ``` -1. Функция получения числового значения должна: +1. Сохранить переданный `ToIntFunction` во внутреннем поле стратегии. - - возвращать значение выбранного поля; - - использоваться для проверки чётности; - - не содержать дополнительной логики сортировки. - -2. Стратегия не должна самостоятельно выбирать поле через: - - - `if`; - - `switch`; - - проверку типа `Student`; - - прямое обращение к конкретным полям `Student`. - -3. Для `Student` доступны функции получения числовых полей: +2. Для определения чётности элемента использовать: + +```java +valueExtractor.applyAsInt(element) +``` + +1. `ToIntFunction` используется только для получения числового значения выбранного поля. Стратегия не должна знать, какое именно поле объекта используется. + +2. Для `Student` в качестве числового поля используется: ```java -Student::getGroupNumber Student::getRecordBookNumber ``` - 1. Поле `averageGrade` не используется для определения чётности, если оно имеет нецелочисленный тип. + 1. `getRecordBookNumber()` должен возвращать `int`. -## Правила сортировки +## Правила алгоритма -1. Элементы с чётным значением выбранного поля участвуют в сортировке. +Стратегия должна: -2. Элементы с нечётным значением выбранного поля не перемещаются. - -3. Чётные элементы должны занимать только те позиции, которые до сортировки принадлежали чётным элементам. - -4. Чётные элементы сортируются в натуральном порядке выбранного числового поля. - -5. Нечётные элементы должны остаться на своих исходных позициях. - -6. Количество элементов коллекции не должно изменяться. +1. Пройти по исходной `MyList`. +2. Определить элементы с чётным значением, используя `ToIntFunction`. +3. Запомнить позиции этих элементов. +4. Отдельно отсортировать только чётные элементы с помощью **Merge Sort** и переданного `Comparator`. +5. Вернуть отсортированные чётные элементы на позиции, которые занимали чётные элементы до сортировки. +6. Не изменять позиции элементов с нечётным значением. +7. Не изменять размер коллекции. ### Пример -Исходная коллекция: +Исходные значения выбранного числового поля: ```text -[3, 8, 5, 2, 4, 7] +[7, 8, 3, 2, 5, 4] +``` + +Чётные элементы: + +```text +8, 2, 4 +``` + +После Merge Sort: + +```text +2, 4, 8 ``` Результат: ```text -[3, 2, 5, 4, 8, 7] +[7, 2, 3, 4, 5, 8] ``` -Нечётные значения `3`, `5`, `7` остались на исходных позициях. +Нечётные элементы `7`, `3`, `5` остались на исходных позициях. -Чётные значения `8`, `2`, `4` были отсортированы по возрастанию и заняли позиции, которые изначально принадлежали чётным элементам. +## Пример использования + +```java +SortStrategy strategy = + new EvenOnlyMergeSortStrategy<>( + Student::getRecordBookNumber + ); + +strategy.sort( + students, + new StudentRecordBookComparator() +); +``` + +Стратегия получает функцию выбора числового поля извне и не содержит логики, специфичной для `Student`. ## Примеры тестов -### Тест сортировки по номеру группы +### Обычный случай -Использовать `Student::getGroupNumber`: +Создать студентов с `recordBookNumber`: -```java -MyList students = ...; - -SortStrategy strategy = - new EvenOnlyMergeSortStrategy<>(Student::getGroupNumber); - -strategy.sort(students, new StudentGroupComparator()); +```text +7, 8, 3, 2, 5, 4 ``` -Проверить, что: +После сортировки ожидается: -- студенты с нечётным номером группы остались на исходных позициях; -- студенты с чётным номером группы отсортированы по возрастанию; +```text +7, 2, 3, 4, 5, 8 +``` + +Проверить: + +- нечётные элементы сохранили свои позиции; +- чётные элементы отсортированы по возрастанию; - количество элементов не изменилось. -### Тест сортировки по номеру зачётной книжки - -Использовать `Student::getRecordBookNumber`. - -Проверить те же условия для номера зачётной книжки. - -### Тест без чётных элементов - -Исходные значения: +### Все значения нечётные ```text -[1, 3, 5, 7] +1, 3, 5, 7 ``` -Результат должен быть идентичен исходной коллекции. - -### Тест без нечётных элементов - -Исходные значения: +Ожидаемый результат: ```text -[8, 2, 6, 4] +1, 3, 5, 7 ``` -Результат: +### Все значения чётные ```text -[2, 4, 6, 8] +8, 2, 6, 4 ``` -### Тест с одним элементом - -Исходные значения: +Ожидаемый результат: ```text -[4] +2, 4, 6, 8 ``` -Результат: +### Один элемент ```text -[4] +4 ``` -### Тест с пустой коллекцией +Ожидаемый результат: -Сортировка пустой коллекции не должна приводить к ошибке. +```text +4 +``` + +### Пустая коллекция + +Пустая коллекция должна остаться пустой и не приводить к ошибке. + +### Проверка `ToIntFunction` + +Передать: + +```java +Student::getRecordBookNumber +``` + +и проверить, что именно `recordBookNumber` используется для определения чётности. + +## Ограничения + +Запрещено: + +- использовать `Arrays.sort()`, `Collections.sort()`, `List.sort()` и другие готовые алгоритмы сортировки; +- перемещать элементы с нечётным значением; +- использовать отдельный алгоритм сортировки вместо Merge Sort; +- напрямую обращаться к полям `Student` внутри `EvenOnlyMergeSortStrategy`; +- выбирать поле через `if` или `switch`; +- использовать стандартные коллекции Java в качестве основной коллекции. ## Результат @@ -153,4 +197,13 @@ strategy.sort(students, new StudentGroupComparator()); EvenOnlyMergeSortStrategy.java ``` -Класс должен принимать `ToIntFunction` через конструктор и работать с существующими `MyList`, `SortStrategy` и компараторами. +Класс должен использовать: + +```text +SortStrategy.java +MyList.java +ToIntFunction +Comparator +``` + +и не должен иметь зависимости от конкретного класса `Student`. From 57868a33a29ab4232f22f00669185484ba24ef85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D0=BE=D0=BB=D0=BE=D0=B4=D0=B8=D0=BD=20=D0=93=D0=B5?= =?UTF-8?q?=D1=80=D0=BC=D0=B0=D0=BD?= Date: Tue, 25 Aug 2026 23:41:58 +0400 Subject: [PATCH 08/14] docs: add student update task --- docs/tasks/StudentUpdate.md | 326 ++++++++++++++++++++++++++++++++++++ 1 file changed, 326 insertions(+) create mode 100644 docs/tasks/StudentUpdate.md diff --git a/docs/tasks/StudentUpdate.md b/docs/tasks/StudentUpdate.md new file mode 100644 index 0000000..e3db385 --- /dev/null +++ b/docs/tasks/StudentUpdate.md @@ -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`; +- `with...()`-операции; +- чтение и запись данных, если они используют `recordBookNumber`. + +Например, существующий: + +```java +implements Comparator +``` + +должен использовать актуальный метод: + +```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...()`, ни внешние классы не должны позволять создать невалидный объект. From 1fe77ef1b3a2600cc0e452fb28addf4b019d3179 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D0=BE=D0=BB=D0=BE=D0=B4=D0=B8=D0=BD=20=D0=93=D0=B5?= =?UTF-8?q?=D1=80=D0=BC=D0=B0=D0=BD?= Date: Tue, 25 Aug 2026 23:56:20 +0400 Subject: [PATCH 09/14] docs: add data source update --- docs/tasks/DataSourceUpdate.md | 81 ++++++++++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 docs/tasks/DataSourceUpdate.md diff --git a/docs/tasks/DataSourceUpdate.md b/docs/tasks/DataSourceUpdate.md new file mode 100644 index 0000000..c175d9c --- /dev/null +++ b/docs/tasks/DataSourceUpdate.md @@ -0,0 +1,81 @@ +# Дополнение к техническому заданию: изменение типа `recordBookNumber` + +Данное дополнение имеет **приоритет над основным ТЗ `DataSource.md`**. + +## Изменение модели `Student` + +Поле: + +```java +private final String recordBookNumber; +``` + +заменено на: + +```java +private final int recordBookNumber; +``` + +Метод: + +```java +getRecordBookNumber() +``` + +возвращает `int`. + +## Требования к `DataSource` + +Во всех реализациях источников данных необходимо учитывать новый тип `recordBookNumber`. + +### `ManualDataSource` + +`recordBookNumber` должен передаваться как `int`: + +```java +setStudent( + int index, + String groupNumber, + double averageGrade, + int recordBookNumber +); +``` + +Строковое значение номера зачётной книжки больше не используется. + +### `RandomDataSource` + +Для `recordBookNumber` необходимо задавать и генерировать **числовой диапазон**: + +```text +минимальное значение — int +максимальное значение — int +``` + +Генерируемое значение должно быть целым числом и соответствовать ограничениям `Student`. + +### `FileDataSource` + +В файле `recordBookNumber` должен записываться как целое число. + +Формат записи: + +```text +groupNumber;averageGrade;recordBookNumber +``` + +Пример: + +```text +A12;4.75;12345 +B32;3.80;12346 +C17;4.20;12347 +``` + +При чтении значение необходимо преобразовать в `int` до создания `Student`. + +## Приоритет + +При противоречии между данным дополнением и основным `DataSource.md` использовать требования этого дополнения. + +Все остальные требования основного технического задания остаются без изменений. From c8c9c05a655f33202764a466be45fd9be6534834 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D0=BE=D0=BB=D0=BE=D0=B4=D0=B8=D0=BD=20=D0=93=D0=B5?= =?UTF-8?q?=D1=80=D0=BC=D0=B0=D0=BD?= Date: Fri, 28 Aug 2026 21:10:13 +0400 Subject: [PATCH 10/14] docs: replace data source task with csv student io task --- docs/tasks/CsvStudentIO.md | 324 ++++++++++++++++++++++++++ docs/tasks/DataSource.md | 399 --------------------------------- docs/tasks/DataSourceUpdate.md | 81 ------- 3 files changed, 324 insertions(+), 480 deletions(-) create mode 100644 docs/tasks/CsvStudentIO.md delete mode 100644 docs/tasks/DataSource.md delete mode 100644 docs/tasks/DataSourceUpdate.md diff --git a/docs/tasks/CsvStudentIO.md b/docs/tasks/CsvStudentIO.md new file mode 100644 index 0000000..89e2ad5 --- /dev/null +++ b/docs/tasks/CsvStudentIO.md @@ -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 +``` + +Конструктор: + +```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 +``` diff --git a/docs/tasks/DataSource.md b/docs/tasks/DataSource.md deleted file mode 100644 index 79a6c14..0000000 --- a/docs/tasks/DataSource.md +++ /dev/null @@ -1,399 +0,0 @@ -# Техническое задание: источники данных - -## Задача - -Реализовать три независимых источника данных для формирования `MyList`: - -- `ManualDataSource`; -- `RandomDataSource`; -- `FileDataSource`. - -Все источники должны формировать только валидные объекты `Student`. - -Класс `Student` содержит: - -```java -private final String groupNumber; -private final double averageGrade; -private final String recordBookNumber; -``` - -Создание объектов `Student` выполнять через `StudentBuilder`. - ---- - -# Общий контракт - -Создать интерфейс `DataSource`: - -```java -public interface DataSource { - - MyList create(); -} -``` - -Источник данных не должен самостоятельно получать данные от пользователя. - -Все необходимые входные значения должны передаваться извне. - -Результатом работы каждого источника является `MyList`. - -Стандартные `List`, `ArrayList` и `LinkedList` для хранения результата не использовать. - ---- - -# `ManualDataSource` - -## Назначение - -Постепенно формировать коллекцию `Student` на основе значений, передаваемых извне для каждого отдельного объекта. - -## Входные данные одного объекта - -Для создания одного `Student` передаются: - -```text -groupNumber -averageGrade -recordBookNumber -``` - -Также передаётся `index` — позиция в коллекции, на которой должен находиться создаваемый объект. - -Размер результирующей коллекции задаётся заранее. - -## Контракт - -`ManualDataSource` должен: - -1. Принимать требуемый размер коллекции. -2. Принимать значения одного `Student`: - - - `groupNumber`; - - `averageGrade`; - - `recordBookNumber`. - -3. Принимать индекс, на котором должен находиться этот объект. -4. Создавать новый `Student` через `StudentBuilder`. -5. Добавлять созданный объект в нужную позицию коллекции. -6. Позволять повторно передавать данные для существующего индекса, заменяя объект на новый. -7. Проверять допустимость индекса. -8. Не изменять уже созданный объект `Student`. -9. Позволять получить текущее состояние коллекции для просмотра. -10. После заполнения вернуть сформированную `MyList`. - -Рекомендуемый метод для передачи одного объекта: - -```java -setStudent( - int index, - String groupNumber, - double averageGrade, - String recordBookNumber -) -``` - -Таким образом, пользовательский код может последовательно передать данные для всех элементов: - -```java -dataSource.setStudent(0, "101", 4.5, "10001"); -dataSource.setStudent(1, "102", 3.8, "10002"); -dataSource.setStudent(2, "101", 4.9, "10003"); -``` - -После заполнения: - -```java -MyList students = dataSource.create(); -``` - -### Изменение объекта - -Для изменения объекта повторно вызывается `setStudent(...)` с нужным индексом: - -```java -dataSource.setStudent(1, "105", 4.2, "10025"); -``` - -Объект на позиции `1` заменяется новым валидным `Student`. - -### Размер коллекции - -Если задан: - -```text -size = 5 -``` - -то для успешного создания коллекции должны быть заданы данные для всех пяти позиций: - -```text -0 -1 -2 -3 -4 -``` - -Порядок заполнения не обязан совпадать с порядком индексов: - -```java -dataSource.setStudent(3, "103", 4.1, "10004"); -dataSource.setStudent(0, "101", 4.5, "10001"); -dataSource.setStudent(4, "102", 3.9, "10005"); -dataSource.setStudent(1, "101", 4.8, "10002"); -dataSource.setStudent(2, "104", 3.7, "10003"); -``` - -После заполнения коллекция должна содержать ровно пять объектов в соответствии с указанными индексами. - -Если хотя бы одна позиция не заполнена, `create()` должен сообщить об ошибке и не возвращать неполную коллекцию. - ---- - -# `RandomDataSource` - -## Назначение - -Создавать заданное количество `Student` со случайными значениями в диапазонах, заданных извне. - -## Входные данные - -Передаются: - -```text -size -``` - -и диапазоны для каждого поля: - -```text -groupNumber: - minimum - maximum - -averageGrade: - minimum - maximum - -recordBookNumber: - minimum - maximum -``` - -## Контракт - -`RandomDataSource` должен: - -1. Принять размер создаваемой коллекции. -2. Принять минимальное и максимальное значение каждого поля. -3. Проверить корректность каждого диапазона. -4. Сгенерировать значения в заданных пределах. -5. Создать каждый объект через `StudentBuilder`. -6. Поместить объекты в `MyList`. -7. Вернуть коллекцию заданного размера. - -Минимальное и максимальное значения включаются в диапазон генерации. - -`RandomDataSource` не должен самостоятельно получать или отображать параметры генерации. - ---- - -# `FileDataSource` - -## Назначение - -Создавать `MyList` на основании содержимого файла. - -## Входные данные - -Передаётся **абсолютный путь к файлу**. - -Относительный путь не является входным параметром `FileDataSource`. - -Формат одной записи: - -```text -groupNumber;averageGrade;recordBookNumber -``` - -Пример: - -```text -101;4.75;123456 -102;3.80;123457 -103;4.20;123458 -``` - -## Контракт - -`FileDataSource` должен: - -1. Получить абсолютный путь к файлу. -2. Открыть файл. -3. Последовательно обработать его записи. -4. Разделить каждую запись на три значения. -5. Создать `Student` через `StudentBuilder`. -6. Добавить только валидные объекты в `MyList`. -7. Пропустить некорректную запись и продолжить обработку остальных. -8. При невозможности открыть файл сообщить об ошибке. -9. Вернуть сформированную `MyList`. - ---- - -# Общие требования - -1. Все источники должны создавать `Student` только через `StudentBuilder`. -2. Источник данных не должен самостоятельно вводить данные. -3. Источник данных не должен зависеть от интерфейса пользователя. -4. Источник данных не должен содержать логику сортировки. -5. Результат хранить только в `MyList`. -6. Невалидный `Student` не должен попасть в коллекцию. -7. Проверка корректности самого объекта выполняется средствами `Student`. -8. Для ручного заполнения изменение существующего `Student` выполнять заменой объекта, а не изменением его полей. - ---- - -# Примеры тестов - -## `ManualDataSource` - -### Заполнение всей коллекции - -Создать источник размера `3`: - -```java -ManualDataSource dataSource = new ManualDataSource(3); - -dataSource.setStudent(0, "101", 4.5, "10001"); -dataSource.setStudent(1, "102", 3.8, "10002"); -dataSource.setStudent(2, "103", 4.2, "10003"); - -MyList result = dataSource.create(); -``` - -Проверить: - -```text -result.size() == 3 -``` - -и соответствие каждого объекта своему индексу. - -### Заполнение в произвольном порядке - -```java -dataSource.setStudent(2, "103", 4.2, "10003"); -dataSource.setStudent(0, "101", 4.5, "10001"); -dataSource.setStudent(1, "102", 3.8, "10002"); -``` - -Проверить правильное расположение объектов. - -### Изменение объекта - -После заполнения: - -```java -dataSource.setStudent(1, "105", 4.9, "10025"); -``` - -Проверить, что изменился только объект с индексом `1`. - -### Незаданный индекс - -Для коллекции размера `3` вызвать: - -```java -dataSource.setStudent(3, ...); -``` - -Проверить выброс исключения. - -### Неполное заполнение - -Заполнить только две позиции из трёх и вызвать `create()`. - -Проверить, что неполная коллекция не возвращается. - ---- - -## `RandomDataSource` - -### Корректная генерация - -Задать размер `100` и допустимые диапазоны для всех трёх полей. - -Проверить: - -```text -result.size() == 100 -``` - -и нахождение каждого значения внутри заданного диапазона. - -### Граничный диапазон - -Задать одинаковые минимальное и максимальное значения. - -Проверить, что все сгенерированные значения соответствуют этому значению. - -### Некорректный диапазон - -Задать: - -```text -minimum > maximum -``` - -Проверить выброс исключения. - ---- - -## `FileDataSource` - -### Корректный файл - -Для файла: - -```text -101;4.75;123456 -102;3.80;123457 -103;4.20;123458 -``` - -проверить получение трёх корректных объектов. - -### Файл с некорректной записью - -```text -101;4.75;123456 -invalid;abc;123457 -103;4.20;123458 -``` - -Проверить, что в коллекцию попадают только две корректные записи. - -### Пустой файл - -Проверить получение пустой коллекции. - -### Несуществующий файл - -Передать путь к несуществующему файлу и проверить корректную обработку ошибки. - ---- - -# Результат - -Реализовать: - -```text -DataSource.java -ManualDataSource.java -RandomDataSource.java -FileDataSource.java -``` - -Допускается создание вспомогательных классов для чтения файла и генерации случайных значений. diff --git a/docs/tasks/DataSourceUpdate.md b/docs/tasks/DataSourceUpdate.md deleted file mode 100644 index c175d9c..0000000 --- a/docs/tasks/DataSourceUpdate.md +++ /dev/null @@ -1,81 +0,0 @@ -# Дополнение к техническому заданию: изменение типа `recordBookNumber` - -Данное дополнение имеет **приоритет над основным ТЗ `DataSource.md`**. - -## Изменение модели `Student` - -Поле: - -```java -private final String recordBookNumber; -``` - -заменено на: - -```java -private final int recordBookNumber; -``` - -Метод: - -```java -getRecordBookNumber() -``` - -возвращает `int`. - -## Требования к `DataSource` - -Во всех реализациях источников данных необходимо учитывать новый тип `recordBookNumber`. - -### `ManualDataSource` - -`recordBookNumber` должен передаваться как `int`: - -```java -setStudent( - int index, - String groupNumber, - double averageGrade, - int recordBookNumber -); -``` - -Строковое значение номера зачётной книжки больше не используется. - -### `RandomDataSource` - -Для `recordBookNumber` необходимо задавать и генерировать **числовой диапазон**: - -```text -минимальное значение — int -максимальное значение — int -``` - -Генерируемое значение должно быть целым числом и соответствовать ограничениям `Student`. - -### `FileDataSource` - -В файле `recordBookNumber` должен записываться как целое число. - -Формат записи: - -```text -groupNumber;averageGrade;recordBookNumber -``` - -Пример: - -```text -A12;4.75;12345 -B32;3.80;12346 -C17;4.20;12347 -``` - -При чтении значение необходимо преобразовать в `int` до создания `Student`. - -## Приоритет - -При противоречии между данным дополнением и основным `DataSource.md` использовать требования этого дополнения. - -Все остальные требования основного технического задания остаются без изменений. From 7c8ff139cf215e57e21d2e4b29deeddad888d671 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D0=BE=D0=BB=D0=BE=D0=B4=D0=B8=D0=BD=20=D0=93=D0=B5?= =?UTF-8?q?=D1=80=D0=BC=D0=B0=D0=BD?= Date: Fri, 28 Aug 2026 21:32:47 +0400 Subject: [PATCH 11/14] docs: add even-only sort strategy task --- docs/tasks/EvenOnlySortStrategy.md | 368 +++++++++++++++++++++++++++++ 1 file changed, 368 insertions(+) create mode 100644 docs/tasks/EvenOnlySortStrategy.md diff --git a/docs/tasks/EvenOnlySortStrategy.md b/docs/tasks/EvenOnlySortStrategy.md new file mode 100644 index 0000000..306c38c --- /dev/null +++ b/docs/tasks/EvenOnlySortStrategy.md @@ -0,0 +1,368 @@ +# Техническое задание: `EvenOnlySortStrategy` + +## Задача + +Создать класс `EvenOnlySortStrategy`, реализующий дополнительное задание по сортировке элементов с чётным значением выбранного числового поля. + +Класс должен использовать **паттерн Стратегия**: он не реализует собственный алгоритм сортировки, а делегирует сортировку переданной реализации `SortStrategy`. + +## Контракт + +Класс: + +```java +public class EvenOnlySortStrategy implements SortStrategy +``` + +Должен содержать два конструктора: + +```java +public EvenOnlySortStrategy() +``` + +```java +public EvenOnlySortStrategy(SortStrategy sortStrategy) +``` + +Также реализовать метод: + +```java +public void setSortStrategy(SortStrategy sortStrategy) +``` + +и основной метод интерфейса `SortStrategy`: + +```java +@Override +public void sort( + MyList list, + Comparator comparator +) +``` + +Для определения чётности числового поля использовать: + +```java +ToIntFunction +``` + +Поэтому рекомендуется также передавать функцию получения числового значения через конструктор или отдельный метод настройки. + +Для `Student` в качестве функции использовать: + +```java +Student::getRecordBookNumber +``` + +## Поведение при отсутствии стратегии + +Если `sortStrategy` не задан: + +```java +EvenOnlySortStrategy strategy = + new EvenOnlySortStrategy<>(); +``` + +то вызов: + +```java +strategy.sort(students, comparator); +``` + +должен завершаться контролируемой ошибкой. + +Рекомендуется выбрасывать: + +```java +IllegalStateException +``` + +с понятным сообщением о том, что стратегия сортировки не задана. + +Не следует использовать `null` как признак успешного выполнения сортировки. + +## Поведение `setSortStrategy` + +Метод: + +```java +setSortStrategy(SortStrategy 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` определить чётность каждого элемента. +3. Запомнить позиции элементов с чётным значением. +4. Сформировать отдельный `MyList` только из чётных элементов. +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 strategy = + new EvenOnlySortStrategy<>( + new MergeSortStrategy<>(), + Student::getRecordBookNumber + ); +``` + +А также после создания заменить стратегию: + +```java +strategy.setSortStrategy(new AnotherSortStrategy<>()); +``` + +После этого специальная сортировка должна использовать новую стратегию без изменения `EvenOnlySortStrategy`. + +## Требования к `ToIntFunction` + +Функция получения числового значения должна передаваться извне. + +Для `Student`: + +```java +Student::getRecordBookNumber +``` + +Стратегия не должна напрямую обращаться к: + +```java +student.getRecordBookNumber() +``` + +или к другим полям `Student`. + +Это позволяет использовать класс с другими типами объектов и другими числовыми полями. + +## Требования к коллекциям + +Работать необходимо с: + +```java +MyList +``` + +Использование `ArrayList`, `LinkedList` и других стандартных коллекций для хранения элементов запрещено. + +Вспомогательная коллекция чётных элементов также должна быть `MyList`. + +## Тесты + +### Переданная стратегия используется + +Передать `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 +MyList +Comparator +ToIntFunction +``` + +и **не должен содержать реализации конкретного алгоритма сортировки**. + +## Git + +Ветка: + +```text +feature/even-only-sort-strategy +``` From 81afad5ecd8779fbfcd9743b1a08498d65509606 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D0=BE=D0=BB=D0=BE=D0=B4=D0=B8=D0=BD=20=D0=93=D0=B5?= =?UTF-8?q?=D1=80=D0=BC=D0=B0=D0=BD?= Date: Fri, 28 Aug 2026 22:28:12 +0400 Subject: [PATCH 12/14] docs: add composite student search task --- docs/tasks/CompositeStudentSearch.md | 455 +++++++++++++++++++++++++++ 1 file changed, 455 insertions(+) create mode 100644 docs/tasks/CompositeStudentSearch.md diff --git a/docs/tasks/CompositeStudentSearch.md b/docs/tasks/CompositeStudentSearch.md new file mode 100644 index 0000000..8ab2a72 --- /dev/null +++ b/docs/tasks/CompositeStudentSearch.md @@ -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 find(MyList students); +``` + +### Поведение + +Метод `find()` должен: + +1. Последовательно проверить студентов исходной коллекции. +2. Передать каждого студента в: + +```java +criteria.matches(student) +``` + +1. Добавить подходящих студентов в новую `MyList`. +2. Вернуть новую коллекцию. + +`StudentSearchService` не должен содержать логику конкретных диапазонов или объединения критериев. + +### Обработка отсутствующего критерия + +Если `StudentSearchService` создан без критерия: + +```java +new StudentSearchService(null); +``` + +необходимо выбросить: + +```java +IllegalArgumentException +``` + +Класс не должен выполнять поиск без заданного критерия. + +--- + +# Пример использования + +### Один критерий + +```java +StudentSearchCriteria criteria = + new AverageGradeRangeCriteria(4.0, 5.0); + +StudentSearchService service = + new StudentSearchService(criteria); + +MyList 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` не изменять. +2. Результат возвращать в виде новой `MyList`. +3. Порядок найденных студентов должен сохраняться. +4. Стандартные `List`, `ArrayList`, `LinkedList` не использовать. +5. Готовые методы поиска не использовать. +6. При отсутствии совпадений возвращать пустую `MyList`. +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 +``` From b6354bf133d6acbfebb75a964d8037331f04bb80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D0=BE=D0=BB=D0=BE=D0=B4=D0=B8=D0=BD=20=D0=93=D0=B5?= =?UTF-8?q?=D1=80=D0=BC=D0=B0=D0=BD?= Date: Fri, 28 Aug 2026 23:11:28 +0400 Subject: [PATCH 13/14] docs: add student occurrence index finder task --- docs/tasks/StudentOccurrenceIndexFinder.md | 688 +++++++++++++++++++++ 1 file changed, 688 insertions(+) create mode 100644 docs/tasks/StudentOccurrenceIndexFinder.md diff --git a/docs/tasks/StudentOccurrenceIndexFinder.md b/docs/tasks/StudentOccurrenceIndexFinder.md new file mode 100644 index 0000000..0c6f202 --- /dev/null +++ b/docs/tasks/StudentOccurrenceIndexFinder.md @@ -0,0 +1,688 @@ +# Техническое задание: многопоточный поиск индексов `Student` + +## Задача + +Реализовать многопоточный поиск всех вхождений заданного объекта `Student` в `MyList`. + +Метод **не должен выводить никаких сообщений в консоль**. + +Результатом работы должен быть массив `int[]`, содержащий индексы всех элементов исходной коллекции, которые равны переданному `Student`. + +Сравнение выполнять через существующий `Student.equals()`. + +--- + +## Входная информация + +Основной метод принимает: + +```java +MyList 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 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 findInRange( + MyList students, + Student target, + int fromIndex, + int toIndex +); +``` + +Обрабатывает диапазон: + +```text +[fromIndex, toIndex) +``` + +и возвращает индексы найденных совпадений. + +--- + +# Работа с многопоточностью + +## Рекомендуемый подход + +Не использовать общий контейнер результатов, к которому одновременно обращаются несколько потоков. + +Также **не рекомендуется использовать мьютексы** (`synchronized`, `Lock`, `ReentrantLock`) для хранения результата. + +Лучше использовать модель: + +```text + MyList + │ + ┌────────────┼────────────┐ + ↓ ↓ ↓ + Поток 1 Поток 2 Поток 3 + диапазон диапазон диапазон + │ │ │ + ↓ ↓ ↓ + MyList MyList MyList + └────────────┼────────────┘ + ↓ + основной поток + ↓ + merge + ↓ + int[] +``` + +Каждый поток создаёт **собственный контейнер `MyList`** и записывает найденные индексы только в него. + +Это позволяет избежать состояния гонки без синхронизации общей структуры. + +--- + +# Разбиение коллекции на потоки + +Количество потоков рекомендуется определять на основании: + +```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 +``` + +Пример: + +```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 +``` + +а объединение выполняется после завершения параллельной части одним потоком. + +--- + +# Рекомендуемые классы и импорты + +Для реализации рекомендуется использовать стандартные средства 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> + ↓ +Future> +``` + +Каждый `Callable` обрабатывает свой диапазон и возвращает собственный `MyList`. + +После получения всех `Future` основной метод объединяет результаты. + +После завершения работы `ExecutorService` должен быть корректно закрыт. + +При `InterruptedException` необходимо восстановить статус прерывания текущего потока: + +```java +Thread.currentThread().interrupt(); +``` + +--- + +# Использование `MyList` + +Сторонние контейнеры **не допускаются**. + +Запрещено использовать: + +```text +ArrayList +LinkedList +Vector +CopyOnWriteArrayList +ConcurrentLinkedQueue +и другие стандартные контейнеры +``` + +как хранилище промежуточных или итоговых результатов. + +Для результатов каждого потока использовать: + +```java +MyList +``` + +Итог преобразовать в: + +```java +int[] +``` + +--- + +# Требуется ли изменять `MyList` + +Текущего интерфейса **достаточно для реализации задачи**: + +```java +public interface MyList { + + 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`, существующего интерфейса достаточно. + +### Важно + +Метод: + +```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. +9. Основной поток получает результаты через Future. +10. Объединяет MyList в порядке диапазонов. +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 +``` + +изменять не требуется. From a8e9855ba64995a6e4301d4b4238f2dc21380a55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D0=BE=D0=BB=D0=BE=D0=B4=D0=B8=D0=BD=20=D0=93=D0=B5?= =?UTF-8?q?=D1=80=D0=BC=D0=B0=D0=BD?= Date: Sat, 29 Aug 2026 11:30:48 +0400 Subject: [PATCH 14/14] docs: remove obsolete EvenOnlyMergeSort task specification --- docs/tasks/EvenOnlyMergeSort.md | 209 -------------------------------- 1 file changed, 209 deletions(-) delete mode 100644 docs/tasks/EvenOnlyMergeSort.md diff --git a/docs/tasks/EvenOnlyMergeSort.md b/docs/tasks/EvenOnlyMergeSort.md deleted file mode 100644 index ad5c4d3..0000000 --- a/docs/tasks/EvenOnlyMergeSort.md +++ /dev/null @@ -1,209 +0,0 @@ -# Техническое задание: сортировка по чётным значениям - -## Задача - -Дополнить существующую сортировку слиянием специальной стратегией `EvenOnlyMergeSortStrategy`. - -Стратегия должна сортировать только объекты, у которых выбранное числовое поле имеет чётное значение. Объекты с нечётным значением должны сохранять свои исходные позиции. - -## Контракты - -1. Стратегия должна реализовывать существующий интерфейс: - -```java -SortStrategy -``` - -1. Метод сортировки должен работать с: - -```java -MyList -``` - -1. Для сортировки чётных элементов использовать алгоритм **Merge Sort**. - -2. Готовые методы сортировки Java использовать запрещено. - -3. Стратегия не должна зависеть непосредственно от класса `Student`. - -4. Числовое поле передавать в стратегию извне через: - -```java -ToIntFunction -``` - -1. Сохранить переданный `ToIntFunction` во внутреннем поле стратегии. - -2. Для определения чётности элемента использовать: - -```java -valueExtractor.applyAsInt(element) -``` - -1. `ToIntFunction` используется только для получения числового значения выбранного поля. Стратегия не должна знать, какое именно поле объекта используется. - -2. Для `Student` в качестве числового поля используется: - -```java -Student::getRecordBookNumber -``` - - 1. `getRecordBookNumber()` должен возвращать `int`. - -## Правила алгоритма - -Стратегия должна: - -1. Пройти по исходной `MyList`. -2. Определить элементы с чётным значением, используя `ToIntFunction`. -3. Запомнить позиции этих элементов. -4. Отдельно отсортировать только чётные элементы с помощью **Merge Sort** и переданного `Comparator`. -5. Вернуть отсортированные чётные элементы на позиции, которые занимали чётные элементы до сортировки. -6. Не изменять позиции элементов с нечётным значением. -7. Не изменять размер коллекции. - -### Пример - -Исходные значения выбранного числового поля: - -```text -[7, 8, 3, 2, 5, 4] -``` - -Чётные элементы: - -```text -8, 2, 4 -``` - -После Merge Sort: - -```text -2, 4, 8 -``` - -Результат: - -```text -[7, 2, 3, 4, 5, 8] -``` - -Нечётные элементы `7`, `3`, `5` остались на исходных позициях. - -## Пример использования - -```java -SortStrategy strategy = - new EvenOnlyMergeSortStrategy<>( - Student::getRecordBookNumber - ); - -strategy.sort( - students, - new StudentRecordBookComparator() -); -``` - -Стратегия получает функцию выбора числового поля извне и не содержит логики, специфичной для `Student`. - -## Примеры тестов - -### Обычный случай - -Создать студентов с `recordBookNumber`: - -```text -7, 8, 3, 2, 5, 4 -``` - -После сортировки ожидается: - -```text -7, 2, 3, 4, 5, 8 -``` - -Проверить: - -- нечётные элементы сохранили свои позиции; -- чётные элементы отсортированы по возрастанию; -- количество элементов не изменилось. - -### Все значения нечётные - -```text -1, 3, 5, 7 -``` - -Ожидаемый результат: - -```text -1, 3, 5, 7 -``` - -### Все значения чётные - -```text -8, 2, 6, 4 -``` - -Ожидаемый результат: - -```text -2, 4, 6, 8 -``` - -### Один элемент - -```text -4 -``` - -Ожидаемый результат: - -```text -4 -``` - -### Пустая коллекция - -Пустая коллекция должна остаться пустой и не приводить к ошибке. - -### Проверка `ToIntFunction` - -Передать: - -```java -Student::getRecordBookNumber -``` - -и проверить, что именно `recordBookNumber` используется для определения чётности. - -## Ограничения - -Запрещено: - -- использовать `Arrays.sort()`, `Collections.sort()`, `List.sort()` и другие готовые алгоритмы сортировки; -- перемещать элементы с нечётным значением; -- использовать отдельный алгоритм сортировки вместо Merge Sort; -- напрямую обращаться к полям `Student` внутри `EvenOnlyMergeSortStrategy`; -- выбирать поле через `if` или `switch`; -- использовать стандартные коллекции Java в качестве основной коллекции. - -## Результат - -Реализовать: - -```text -EvenOnlyMergeSortStrategy.java -``` - -Класс должен использовать: - -```text -SortStrategy.java -MyList.java -ToIntFunction -Comparator -``` - -и не должен иметь зависимости от конкретного класса `Student`.