Работа со сложными данными #

Васильев Андрей Михайлович, 2025

Версии презентации

Проблема большого класса-хранилища #

В рамках HTTP-обработчика необходимо иметь доступ к хранилищу данных, т.к. все действия приложения так или иначе сводятся к выборке или изменению набора данных

При использовании большого класса-хранилища

diagram

  • В хранилище начинают оседать все функции, связанные с обработкой данных внутри
  • При написании теста обработчика ему необходимо предоставить тестовый дубль класса-хранилища, а может быть и не одного
  • Остаётся нерешённым вопрос хранения кода, которому необходимо взаимодействовать со множеством хранилищ одновременно

Слой операций #

Добавление промежуточного слоя, классов операций, позволяет решить ряд проблем

diagram

  • Классы-операции должны решать только одну задачу
  • В приложении может быть на порядок больше операций по сравнению с количеством хранилищ, но каждая из них будет простой и понятной
  • Операции могут взаимодействовать со множеством хранилищ для решения своей задачи

Слой операций #

  • На уровне хранилища решается задача по хранению набора объектов
  • На уровне хранилища решается задача по изменению набора объектов
  • Сложные действия, включающие любую обработку данных, располагаются в соответствующих операциях
  • Операция не является декорацией для вызова функции в хранилище

В сравнении с СУБД можно провести следующие аналогии

  • Хранилище представляет собой БД
  • Операция представляет собой логику по выполнению запроса к БД

В рамках данных лабораторных работ хранилище де-факто имитирует СУБД

Операции на классах #

Операции можно описать с помощью классов

CalculateTriangleStatistics(private val triangleStorage: TriangleStorage) {
    fun statistics(color: Color): Map<Int, Double> {
        val allTriangles = triangleStorage.list()
        ...
    }
}
val operation = CalculateTriangleStatistics(TriangleStorage)

В рамках HTTP-обработчика необходимо обеспечить доступ к объекту-операции:

class ShowStatisticsHandler(
    calculateTriangleStatistics: CalculateTriangleStatistics,
) : HttpHandler { request ->
    val color: Color = ....
    val statistics = calculateTriangleStatistics.statistics(color)
    ...
}

В рамках автоматических тестов достаточно сформировать мок-объект операции, который будет возвращать необходимые данные при запросе

Операции на функциях #

Операции можно описать с помощью функций

fun calculateTriangleStatistics(triangleStorage: TriangleStorage):
    (Color) -> Map<Int, Double> = { color ->
        val allTriangles = triangleStorage.list()
        ...
    }
val operation = calculateTriangleStatistics(triangleStorage)

Внутри HTTP-обработчика необходимо обеспечить доступ к операции:

fun showStatisticsHandler(
    calculateTriangleStatistics: (Color) -> Map<Int, Dobule>,
): HttpHandler = { request -> 
    val color: Color = ...
    val statistics = calculateTriangleStatistics(color)
}

При написании автоматических тестов в HTTP-обработчик необходимо передать функцию, которая будет возвращать данные, нужные для тестов