Рефакторинг логики шифрования/дешифрования файлов с использованием паттерна Strategy
Введение
В одном из недавних проектов мы заменили существующее решение шифрования файлов с DRM на новую систему на основе MIP.
Если бы все файлы просто обрабатывались через MIP, задача была бы достаточно простой. Однако одним из ключевых требований было сохранение совместимости с устаревшими DRM-файлами, которые уже были скачаны ранее.
Иными словами, при загрузке файлов должны были одновременно сосуществовать два механизма — DRM и MIP — и динамически применяться в зависимости от ситуации.
Попытки вписать это требование в существующую архитектуру выявили ряд ограничений. В результате мы провели рефакторинг системы, применив объектно-ориентированный подход с использованием паттерна Strategy, и хотели бы поделиться этим опытом.
Проблемы существующего кода: ограничения статических утилит
Изначально логика шифрования и дешифрования была реализована в виде статических методов внутри общего утилитного класса.
Хотя можно было просто добавить условные ветвления для поддержки новых требований, стало очевидно, что такой подход приведёт к ряду архитектурных проблем:
1. Риск «shotgun surgery» и высокая связанность
Логика шифрования/дешифрования использовалась более чем в 12 местах бизнес-логики.
При сохранении текущего подхода любое изменение или добавление нового метода шифрования потребовало бы модификации всех 12 мест вызова. Это классический пример «shotgun surgery», приводящий к жёстко связанной системе.
2. Отсутствие консистентности параметров и слабая расширяемость
DRM и MIP требуют разные наборы параметров.
Если передавать их как отдельные параметры в одном методе, при добавлении нового решения сигнатура метода будет меняться, ломая существующий код. Для обеспечения гибкости и согласованности параметры необходимо было объединить в единый DTO.
3. Сложности в написании unit-тестов
Статические методы не имеют состояния и плохо поддаются мокированию, что затрудняет написание изолированных unit-тестов для бизнес-логики.
Решение: разделение изменяемого и неизменяемого с помощью Strategy Pattern
Бизнес-логика должна гибко изменяться в зависимости от требований, однако статическая структура этому препятствовала.
Чтобы фундаментально решить проблему, мы переработали архитектуру, применив ключевой принцип: разделение того, что изменяется, и того, что остаётся неизменным.
Для этого был использован паттерн Strategy, с чётким разделением ответственности:
- Изменяемое: конкретные алгоритмы шифрования/дешифрования (DRM, MIP), которые могут быть заменены или расширены
- Неизменяемое: сам процесс шифрования/дешифрования и единая структура параметров
В результате вместо прямой зависимости от конкретных алгоритмов мы ввели абстракцию через интерфейсы.
Это позволило 12 клиентским компонентам работать независимо от конкретной реализации шифрования.
На практике был определён интерфейс с методами для шифрования, дешифрования и определения типа шифрования. Реализации DRM и MIP наследуют этот интерфейс и предоставляют свою логику.
Эти реализации зарегистрированы как Spring Beans, что обеспечивает их удобное использование в разных модулях.
Заключение и ожидаемые преимущества
После рефакторинга все 12 клиентских компонентов взаимодействуют только с абстракцией FileEncryptor, не зная о конкретной реализации.
Это позволило реализовать принцип Open-Closed.
Отказ от статических методов значительно упростил unit-тестирование.
Кроме того, при добавлении нового механизма шифрования не потребуется изменять существующую бизнес-логику.
Этот опыт стал не просто решением текущей задачи, но и возможностью глубже задуматься о расширяемости системы и долгосрочной поддерживаемости.
Спасибо.
pong