Skip to content

Реализация задач ТЗ спринта 8#6

Merged
h0ttab merged 47 commits intomainfrom
sprint_8-solution-time-and-duration
Jun 25, 2025
Merged

Реализация задач ТЗ спринта 8#6
h0ttab merged 47 commits intomainfrom
sprint_8-solution-time-and-duration

Conversation

@h0ttab
Copy link
Owner

@h0ttab h0ttab commented Jun 24, 2025

Что было сделано

Согласно ТЗ финального задания спринта 8 в приложение были внесены следующие изменения:

  • В классы задач были добавлены новые поля для хранения даты/времени начала выполнения задачи, её продолжительности, и вычисляемая дата окончания выполнения задачи
    • Для класса Epic все новые поля - вычисляемые, и рассчитываются на основании входящих в него подзадач
  • В менеджер была добавлена функция получения задач и подзадач в порядке приоритета. Хранение отсортированного перечня задач реализовано через TreeSet.
  • Добавлена возможность создавать задачи без указания времени начала. Такие задачи не попадают в список приоритетных задач.
  • Добавлена проверка пересечений. Проверка реализована более оптимальным способом, чем предложено в ТЗ (см. "Дополнительно").
  • Там, где это было целесообразно, использование циклов было заменено на forEach/StreamAPI.
  • Были актуализированны демонстрационные сценарии в методах main().
  • Были обновлены старые, и добавлены новые Unit-тесты.
  • Общие тесты для интерфейса TaskManager были вынесены в абстрактный класс.
  • Были выполнены два дополнительных задания (см. "Дополнительно").

Дополнительно:

  • В класс IdGenerator был добавлен механизм перераспределения ID, которые освободились после удаления задач. Это позволяет расходовать назначаемые ID со 100% КПД. Когда вызывается метод генерации нового ID, сначала проверяется, нет ли старых свободных ID, и если есть - отдаётся свободный ID, если нет - только тогда создаётся новый.
  • В ТЗ не было указано, что делать, если добавляемая задача имеет пересечение с существующими задачами. В моей реализации такие задачи добавляются, но у них удаляется указание времени начала, чтобы они не попадали в список приоритетных задач.

В ходе работы на задачей было принято несколько компромиссных решений, не совсем точно соответствующих требованиям ТЗ:

Что:
Эпик не хранит отсортированный список своих подзадач для расчёта вычисляемых полей.
Почему:
Я решил не уходить от принципа разграничения слоёв приложения. Эпик не должен "знать" про менеджер или про подзадачи. Эпик хранит только ID (int) своих подздач, и управляется менеджером.
Как сделал:
Когда нужно обновить вычисляемые поля, менеджер вызывает у нужного эпика соответствующий метод и передаёт в виде аргумента уже отсортированный список его подзадач (менеджер знает про все эпики и все подзадачи).

Что:
Проверка на пересечение времени выполнения задач не использует StreamAPI и метод-предикат для сравнения. Дополнительное задание на поиск более оптимального алгоритма поиска пересечени за O(1) выполнено частично.
Почему:
Я рассматривал три возможных варианта поиска пересечений:
- StreamAPI + метод-предикат для сравнения. Относительно лаконичный и понятный способ, работает за O(n).
- Поиск "соседей" по дереву, и сравнение добавляемой задачи с задачей ДО и ПОСЛЕ неё на временной линии. Чуть более многословный, но работает за O(log n).
- Предложенный в подсказке к доп. заданию вариант через создание мапы со слотами бронирования интервалов по 15 минут. Очень многословный способ, потребуется написание большого количества кода, который потом легко может создать проблемы, но зато работает за O(k), где k - количество интервалов, которые нужно будет проверить. Будем считать, что это амортизированная O(1) сложность.
Я остановился на варианте поиска соседних задач в дереве. O(n) - намного медленнее чем O(log n) при минимальной разнице в сложности реализации, а вариант с O(1) даёт избыточную производительность, и при этом накладывает массу ограничений: чем точнее нужна сетка слотов, тем больше разрастётся структура для их хранения; ограничение времени планирования (а что, если я хочу запланировать задачу на 1 год и 1 день вперёд? переписывать метод?); если задача имеет длительность 16 минут, то она займёт два слота по 15 минут.
Как сделал:
Через методы TreeSet floor и ceiling находим потенциальных соседей по временной линии для добавляемой задачи, и проверяем пересечения только с ними.

Что:
В тесты не была добавлена проверка обязательного наличия связанного эпика у подзадачи.
Почему:
Аргумент epicId является обязательным в конструкторе подзадачи, но если жёстко проверять его на null - может всё поломаться при чтении из CSV, т.к. там может быть любая очерёдность добавления эпиков и подзадач. Например, подзадача уже загружена, а её эпик ещё нет - будет ложное срабатывание проверки.
Как сделал:
Невозможность создать подзадачу без эпика заложена на уровне архитектуры классов.

h0ttab added 30 commits June 22, 2025 16:46
@h0ttab h0ttab merged commit bd0fae3 into main Jun 25, 2025
1 check passed
@h0ttab h0ttab deleted the sprint_8-solution-time-and-duration branch June 25, 2025 14:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants