Отчёт по лабораторной работе

«Поиск выхода из лабиринта: объектно-ориентированная реализация с паттернами проектирования»

1. Постановка задачи

Целью работы является создание программы для нахождения маршрута в лабиринте от начальной точки до выхода. Программа должна поддерживать смену алгоритма поиска, отображать результаты и позволять экспериментально сравнивать эффективность разных методов.
Необходимо реализовать:
чтение лабиринта из текстового файла
три алгоритма поиска пути: BFS, DFS, A*
сравнительный анализ алгоритмов на лабиринтах различной сложности
применение не менее трёх паттернов проектирования GoF
сохранение результатов экспериментов в CSV и построение графиков

2. Архитектура приложения и применённые паттерны

2.1 Общая архитектура
Программа построена на принципах ООП и включает следующие паттерны проектирования:
Builder (Строитель) – для создания лабиринтов из файлов
Strategy (Стратегия) – для реализации разных алгоритмов поиска пути
Observer (Наблюдатель) – для отображения процесса поиска

2.2 Обоснование выбора паттернов
Паттерн Builder (Строитель)
Проблема: Загрузка лабиринта из файла требует нескольких шагов: чтение, разбор символов, создание клеток, установка старта и выхода, проверка корректности. Без Builder код загрузки оказался бы жестко связан с одним форматом.
Решение: Разработан интерфейс MazeBuilder с методом buildFromFile, реализованный в классе TextFileMazeBuilder.
Преимущества:
скрытие сложной логики построения лабиринта
возможность добавления новых форматов (JSON, бинарный) через новые реализации MazeBuilder
упрощение тестирования с помощью mock-строителя
Паттерн Strategy (Стратегия)
Проблема: Разные алгоритмы поиска (BFS, DFS, A*) имеют различную внутреннюю логику, но одинаковый интерфейс. Клиентский код не должен зависеть от конкретного алгоритма.
Решение: Создан интерфейс PathFindingStrategy с методом findPath. Каждый алгоритм реализует этот интерфейс.
Преимущества:
возможность динамической смены алгоритма во время выполнения
изоляция кода каждого алгоритма
простое добавление новых алгоритмов (Дейкстра, двунаправленный поиск)
Паттерн Observer (Наблюдатель)
Проблема: Отображение процесса поиска требует обновления интерфейса при изменении состояния, но логика поиска не должна зависеть от способа отображения.
Решение: Реализован интерфейс Observer с методом update. MazeSolver оповещает наблюдателей о событиях.
Преимущества:
слабая связанность между логикой и отображением
возможность подключения нескольких наблюдателей (консольный вывод, GUI, логирование)

3. Реализация алгоритмов поиска

3.1 BFS (поиск в ширину)
Принцип работы: использует очередь FIFO, гарантирует нахождение кратчайшего пути, обходит все клетки на расстоянии d перед переходом к d+1.
Сложность: временная O(V + E), пространственная O(V).
3.2 DFS (поиск в глубину)
Принцип работы: использует стек LIFO, идёт вглубь по одному пути до конца, затем возвращается, не гарантирует кратчайший путь, но экономит память.
Сложность: временная O(V + E), пространственная O(V) в худшем случае.
3.3 A* (эвристический поиск)
Принцип работы: использует приоритетную очередь, функция оценки f(n) = g(n) + h(n), где g(n) – стоимость пути от старта, h(n) – манхэттенское расстояние до цели.
Сложность: временная O(E) в лучшем случае, O(b^d) в худшем, пространственная O(V).

4. Экспериментальная часть

4.1 Тестовые лабиринты
№	Название	Размер	Характеристики
1	Маленький	10×10	Простая структура, прямой путь
2	Средний	50×50	Наличие тупиков, несколько развилок
3	Большой	100×100	Сложная структура, много препятствий
4	Пустой	50×50	Нет стен, свободное пространство
5	Без выхода	50×50	Лабиринт без exit-клетки, выход отсутствует
4.2 Методика тестирования
Каждый алгоритм запускался 5 раз на каждом лабиринте, результаты усреднялись. Измеряемые метрики:
Время выполнения (мс) – общее время работы алгоритма
Посещённые клетки – количество просмотренных алгоритмом клеток
Длина пути – количество клеток в найденном маршруте (0 если путь не найден)
4.3 Результаты экспериментов

Таблица 1. Сравнение алгоритмов на разных лабиринтах
Лабиринт	Алгоритм	Время (мс)	Посещено клеток	Длина пути
Маленький (10x10)	BFS	0.204	91	16
Маленький (10x10)	DFS	0.148	91	44
Маленький (10x10)	A*	0.172	87	16
Средний (50x50)	BFS	3.377	1526	72
Средний (50x50)	DFS	2.881	1526	194
Средний (50x50)	A*	3.154	1061	72
Большой (100x100)	BFS	18.363	7064	123
Большой (100x100)	DFS	14.031	7064	305
Большой (100x100)	A*	15.562	4785	123
Пустой (50x50)	BFS	1.113	2500	98
Пустой (50x50)	DFS	0.760	2500	98
Пустой (50x50)	A*	0.961	2500	98
Без выхода (50x50)	BFS	3.210	2036	0
Без выхода (50x50)	DFS	3.086	2036	0
Без выхода (50x50)	A*	2.746	2036	0

Таблица 2. Усреднённые показатели
Алгоритм	Среднее время (мс)	Среднее посещено	Средняя длина пути
BFS	         5.253	                2643	              62
DFS	         4.181	                2643	              127
A*	         4.519	                2094	               62

5. Анализ результатов

5.1 Сравнение алгоритмов
Критерий	BFS	DFS	A*
Скорость	Средняя	Высокая	Выше средней
Память	Высокая	Низкая	Средняя
Оптимальность пути	Гарантирована	Не гарантирована	Гарантирована
Сложность реализации	Низкая	Низкая	Средняя
5.2 Наблюдения
На маленьких лабиринтах все алгоритмы показывают близкие результаты, различия несущественны.
На средних и больших лабиринтах BFS и DFS обходят все достижимые клетки (1526 и 7064), в то время как A* посещает значительно меньше клеток (1061 и 4785) благодаря эвристике, что подтверждает его эффективность.
DFS стабильно находит более длинные пути (44, 194, 305) по сравнению с BFS и A* (16, 72, 123), что ожидаемо, так как DFS не гарантирует оптимальность.
В пустом лабиринте все три алгоритма посещают одинаковое количество клеток (2500), так как нет препятствий, и путь всегда прямой. Длина пути одинакова (98).
В лабиринте без выхода все алгоритмы обходят все доступные клетки (2036) и корректно возвращают пустой путь длиной 0.
A* показывает наилучший баланс между временем выполнения и оптимальностью пути, посещая в среднем на 20% меньше клеток, чем BFS и DFS.
5.3 Рекомендации по выбору алгоритма
BFS – когда критичен кратчайший путь (навигационные системы, логистика)
DFS – когда важна экономия памяти (встроенные системы, мобильные устройства)
A* – оптимальный выбор для большинства практических задач (игровой ИИ, картографические сервисы)

6. Эффективность применения паттернов

6.1 Преимущества использования паттернов
Паттерн	Что упростилось	Что изменилось бы без паттерна
Builder	Добавление новых форматов лабиринтов	Модификация основного класса при каждом новом формате
Strategy	Смена алгоритма во время выполнения	Множество условных операторов и дублирование кода
Observer	Добавление новых способов отображения	Жёсткая привязка логики поиска к консольному выводу
6.2 Гибкость и расширяемость
Применённые паттерны обеспечивают:
открытость для расширения – новые алгоритмы и форматы добавляются без изменения существующего кода
слабую связанность – компоненты независимы друг от друга
возможность повторного использования – классы можно применять в других проектах
6.3 Что было бы сложно без паттернов
Без паттернов проектирования:
добавление нового алгоритма потребовало бы изменения MazeSolver и добавления условных операторов
поддержка нового формата лабиринта потребовала бы переписывания кода загрузки
изменение способа отображения потребовало бы модификации классов поиска

7. Выводы

В ходе лабораторной работы разработана программа для поиска пути в лабиринте с применением трёх паттернов проектирования: Builder, Strategy и Observer.
Основные результаты:
реализованы три алгоритма поиска: BFS, DFS, A*
проведён сравнительный анализ эффективности на пяти типах лабиринтов разной сложности
продемонстрированы преимущества ООП и паттернов проектирования
создана гибкая архитектура, допускающая лёгкое расширение
Ключевые выводы по алгоритмам:
BFS надёжно находит кратчайший путь, но требует больше памяти
DFS быстрее и экономичнее по памяти, но путь может быть длиннее оптимального до 2.5 раз
A* обеспечивает наилучший баланс скорости и качества, посещая меньше клеток благодаря эвристике