Рефакторинг логики шифрования/дешифрования файлов с использованием паттерна Strategy

Рефакторинг логики шифрования/дешифрования файлов с использованием паттерна 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