456 lines
10 KiB
Markdown
456 lines
10 KiB
Markdown
|
|
# Техническое задание: поиск студентов с использованием паттерна Компоновщик
|
|||
|
|
|
|||
|
|
## Задача
|
|||
|
|
|
|||
|
|
Реализовать механизм поиска студентов по диапазону значений с использованием паттерна **Компоновщик**.
|
|||
|
|
|
|||
|
|
Должны поддерживаться следующие критерии поиска:
|
|||
|
|
|
|||
|
|
- диапазон `groupNumber`;
|
|||
|
|
- диапазон `averageGrade`;
|
|||
|
|
- диапазон `recordBookNumber`.
|
|||
|
|
|
|||
|
|
Критерии должны поддерживать объединение в составной критерий.
|
|||
|
|
|
|||
|
|
Режим объединения критериев — **объединение (ИЛИ)** или **пересечение (И)** — задаётся в конструкторе основного класса.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
# Названия классов и интерфейсов
|
|||
|
|
|
|||
|
|
## `StudentSearchCriteria`
|
|||
|
|
|
|||
|
|
Интерфейс отдельного критерия поиска.
|
|||
|
|
|
|||
|
|
### Контракт
|
|||
|
|
|
|||
|
|
```java
|
|||
|
|
boolean matches(Student student);
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Метод возвращает `true`, если студент соответствует критерию.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## `GroupNumberRangeCriteria`
|
|||
|
|
|
|||
|
|
Критерий поиска по диапазону `groupNumber`.
|
|||
|
|
|
|||
|
|
### Конструктор
|
|||
|
|
|
|||
|
|
```java
|
|||
|
|
GroupNumberRangeCriteria(String min, String max);
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Метод
|
|||
|
|
|
|||
|
|
```java
|
|||
|
|
boolean matches(Student student);
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
`groupNumber` имеет тип `String`, поэтому сравнение выполнять лексикографически.
|
|||
|
|
|
|||
|
|
Границы диапазона включаются.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## `AverageGradeRangeCriteria`
|
|||
|
|
|
|||
|
|
Критерий поиска по диапазону `averageGrade`.
|
|||
|
|
|
|||
|
|
### Конструктор
|
|||
|
|
|
|||
|
|
```java
|
|||
|
|
AverageGradeRangeCriteria(double min, double max);
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Метод
|
|||
|
|
|
|||
|
|
```java
|
|||
|
|
boolean matches(Student student);
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Студент соответствует критерию, если:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
min <= averageGrade <= max
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## `RecordBookNumberRangeCriteria`
|
|||
|
|
|
|||
|
|
Критерий поиска по диапазону `recordBookNumber`.
|
|||
|
|
|
|||
|
|
### Конструктор
|
|||
|
|
|
|||
|
|
```java
|
|||
|
|
RecordBookNumberRangeCriteria(int min, int max);
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Метод
|
|||
|
|
|
|||
|
|
```java
|
|||
|
|
boolean matches(Student student);
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Студент соответствует критерию, если:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
min <= recordBookNumber <= max
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
# `CompositeStudentSearchCriteria`
|
|||
|
|
|
|||
|
|
Компоновщик критериев поиска.
|
|||
|
|
|
|||
|
|
Класс реализует:
|
|||
|
|
|
|||
|
|
```java
|
|||
|
|
StudentSearchCriteria
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
и содержит другие реализации `StudentSearchCriteria`.
|
|||
|
|
|
|||
|
|
### Методы
|
|||
|
|
|
|||
|
|
```java
|
|||
|
|
void add(StudentSearchCriteria criteria);
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
```java
|
|||
|
|
void remove(StudentSearchCriteria criteria);
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
```java
|
|||
|
|
boolean matches(Student student);
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Логика
|
|||
|
|
|
|||
|
|
`CompositeStudentSearchCriteria` должен поддерживать два режима:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
UNION — объединение условий, ИЛИ
|
|||
|
|
INTERSECTION — пересечение условий, И
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Режим должен задаваться при создании компоновщика:
|
|||
|
|
|
|||
|
|
```java
|
|||
|
|
CompositeStudentSearchCriteria(
|
|||
|
|
SearchOperation operation
|
|||
|
|
);
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
где `SearchOperation` — перечисление:
|
|||
|
|
|
|||
|
|
```java
|
|||
|
|
public enum SearchOperation {
|
|||
|
|
UNION,
|
|||
|
|
INTERSECTION
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Для `UNION` студент подходит, если выполняется **хотя бы один** вложенный критерий.
|
|||
|
|
|
|||
|
|
Для `INTERSECTION` студент подходит, если выполняются **все** вложенные критерии.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
# `StudentSearchService`
|
|||
|
|
|
|||
|
|
Основной класс поиска.
|
|||
|
|
|
|||
|
|
Он должен принимать интерфейс `StudentSearchCriteria`, а не конкретную реализацию критерия.
|
|||
|
|
|
|||
|
|
### Конструктор
|
|||
|
|
|
|||
|
|
```java
|
|||
|
|
StudentSearchService(StudentSearchCriteria criteria);
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Метод
|
|||
|
|
|
|||
|
|
```java
|
|||
|
|
MyList<Student> find(MyList<Student> students);
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Поведение
|
|||
|
|
|
|||
|
|
Метод `find()` должен:
|
|||
|
|
|
|||
|
|
1. Последовательно проверить студентов исходной коллекции.
|
|||
|
|
2. Передать каждого студента в:
|
|||
|
|
|
|||
|
|
```java
|
|||
|
|
criteria.matches(student)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
1. Добавить подходящих студентов в новую `MyList<Student>`.
|
|||
|
|
2. Вернуть новую коллекцию.
|
|||
|
|
|
|||
|
|
`StudentSearchService` не должен содержать логику конкретных диапазонов или объединения критериев.
|
|||
|
|
|
|||
|
|
### Обработка отсутствующего критерия
|
|||
|
|
|
|||
|
|
Если `StudentSearchService` создан без критерия:
|
|||
|
|
|
|||
|
|
```java
|
|||
|
|
new StudentSearchService(null);
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
необходимо выбросить:
|
|||
|
|
|
|||
|
|
```java
|
|||
|
|
IllegalArgumentException
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Класс не должен выполнять поиск без заданного критерия.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
# Пример использования
|
|||
|
|
|
|||
|
|
### Один критерий
|
|||
|
|
|
|||
|
|
```java
|
|||
|
|
StudentSearchCriteria criteria =
|
|||
|
|
new AverageGradeRangeCriteria(4.0, 5.0);
|
|||
|
|
|
|||
|
|
StudentSearchService service =
|
|||
|
|
new StudentSearchService(criteria);
|
|||
|
|
|
|||
|
|
MyList<Student> result =
|
|||
|
|
service.find(students);
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Пересечение критериев
|
|||
|
|
|
|||
|
|
Например, студент должен одновременно:
|
|||
|
|
|
|||
|
|
- находиться в группе от `A10` до `C30`;
|
|||
|
|
- иметь средний балл от `4.0` до `5.0`.
|
|||
|
|
|
|||
|
|
```java
|
|||
|
|
CompositeStudentSearchCriteria criteria =
|
|||
|
|
new CompositeStudentSearchCriteria(
|
|||
|
|
SearchOperation.INTERSECTION
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
criteria.add(
|
|||
|
|
new GroupNumberRangeCriteria("A10", "C30")
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
criteria.add(
|
|||
|
|
new AverageGradeRangeCriteria(4.0, 5.0)
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
StudentSearchService service =
|
|||
|
|
new StudentSearchService(criteria);
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Объединение критериев
|
|||
|
|
|
|||
|
|
Студент должен соответствовать хотя бы одному условию:
|
|||
|
|
|
|||
|
|
```java
|
|||
|
|
CompositeStudentSearchCriteria criteria =
|
|||
|
|
new CompositeStudentSearchCriteria(
|
|||
|
|
SearchOperation.UNION
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
criteria.add(
|
|||
|
|
new AverageGradeRangeCriteria(4.5, 5.0)
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
criteria.add(
|
|||
|
|
new RecordBookNumberRangeCriteria(10000, 20000)
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
StudentSearchService service =
|
|||
|
|
new StudentSearchService(criteria);
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
# Общие требования
|
|||
|
|
|
|||
|
|
1. `StudentSearchService` должен работать только через `StudentSearchCriteria`.
|
|||
|
|
2. `StudentSearchService` не должен знать о конкретных критериях.
|
|||
|
|
3. `CompositeStudentSearchCriteria` должен реализовывать тот же интерфейс `StudentSearchCriteria`.
|
|||
|
|
4. Должна поддерживаться вложенность компоновщиков.
|
|||
|
|
|
|||
|
|
Например:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
Composite
|
|||
|
|
├── GroupNumberRangeCriteria
|
|||
|
|
└── Composite
|
|||
|
|
├── AverageGradeRangeCriteria
|
|||
|
|
└── RecordBookNumberRangeCriteria
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
1. Исходную `MyList<Student>` не изменять.
|
|||
|
|
2. Результат возвращать в виде новой `MyList<Student>`.
|
|||
|
|
3. Порядок найденных студентов должен сохраняться.
|
|||
|
|
4. Стандартные `List`, `ArrayList`, `LinkedList` не использовать.
|
|||
|
|
5. Готовые методы поиска не использовать.
|
|||
|
|
6. При отсутствии совпадений возвращать пустую `MyList<Student>`.
|
|||
|
|
7. Для диапазонов `min > max` выбрасывать `IllegalArgumentException`.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
# Примеры тестов
|
|||
|
|
|
|||
|
|
## `GroupNumberRangeCriteria`
|
|||
|
|
|
|||
|
|
Проверить:
|
|||
|
|
|
|||
|
|
- значение внутри диапазона;
|
|||
|
|
- значение равное нижней границе;
|
|||
|
|
- значение равное верхней границе;
|
|||
|
|
- значение вне диапазона.
|
|||
|
|
|
|||
|
|
## `AverageGradeRangeCriteria`
|
|||
|
|
|
|||
|
|
Проверить:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
3.9 → false
|
|||
|
|
4.0 → true
|
|||
|
|
4.5 → true
|
|||
|
|
5.0 → true
|
|||
|
|
5.1 → false
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## `RecordBookNumberRangeCriteria`
|
|||
|
|
|
|||
|
|
Проверить значения внутри и вне диапазона, включая обе границы.
|
|||
|
|
|
|||
|
|
## Некорректный диапазон
|
|||
|
|
|
|||
|
|
Для каждого критерия проверить:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
min > max
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Ожидается:
|
|||
|
|
|
|||
|
|
```java
|
|||
|
|
IllegalArgumentException
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## `INTERSECTION`
|
|||
|
|
|
|||
|
|
Создать два критерия и проверить:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
критерий A = true
|
|||
|
|
критерий B = true
|
|||
|
|
→ true
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
критерий A = true
|
|||
|
|
критерий B = false
|
|||
|
|
→ false
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## `UNION`
|
|||
|
|
|
|||
|
|
Проверить:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
A = true
|
|||
|
|
B = false
|
|||
|
|
→ true
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
A = false
|
|||
|
|
B = true
|
|||
|
|
→ true
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
A = false
|
|||
|
|
B = false
|
|||
|
|
→ false
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## Компоновщик без критериев
|
|||
|
|
|
|||
|
|
Проверить поведение:
|
|||
|
|
|
|||
|
|
```java
|
|||
|
|
new CompositeStudentSearchCriteria(
|
|||
|
|
SearchOperation.INTERSECTION
|
|||
|
|
);
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
и:
|
|||
|
|
|
|||
|
|
```java
|
|||
|
|
new CompositeStudentSearchCriteria(
|
|||
|
|
SearchOperation.UNION
|
|||
|
|
);
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Поведение должно быть явно определено и закреплено тестами.
|
|||
|
|
|
|||
|
|
Рекомендуется:
|
|||
|
|
|
|||
|
|
- пустое `INTERSECTION` → `true`;
|
|||
|
|
- пустое `UNION` → `false`.
|
|||
|
|
|
|||
|
|
## `StudentSearchService`
|
|||
|
|
|
|||
|
|
Проверить:
|
|||
|
|
|
|||
|
|
- корректный поиск по одному критерию;
|
|||
|
|
- пустой результат;
|
|||
|
|
- пустую исходную коллекцию;
|
|||
|
|
- сохранение порядка;
|
|||
|
|
- неизменность исходной коллекции;
|
|||
|
|
- работу с `CompositeStudentSearchCriteria`.
|
|||
|
|
|
|||
|
|
## Проверка вложенного Компоновщика
|
|||
|
|
|
|||
|
|
Создать компоновщик с вложенным компоновщиком и проверить корректность вычисления результата.
|
|||
|
|
|
|||
|
|
## Проверка отсутствующего критерия
|
|||
|
|
|
|||
|
|
Создать:
|
|||
|
|
|
|||
|
|
```java
|
|||
|
|
new StudentSearchService(null);
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Проверить `IllegalArgumentException`.
|
|||
|
|
|
|||
|
|
## Результат
|
|||
|
|
|
|||
|
|
Реализовать:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
StudentSearchCriteria.java
|
|||
|
|
GroupNumberRangeCriteria.java
|
|||
|
|
AverageGradeRangeCriteria.java
|
|||
|
|
RecordBookNumberRangeCriteria.java
|
|||
|
|
CompositeStudentSearchCriteria.java
|
|||
|
|
SearchOperation.java
|
|||
|
|
StudentSearchService.java
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## Git
|
|||
|
|
|
|||
|
|
Название ветки:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
feature/composite-student-search
|
|||
|
|
```
|