Редактирование данных

Редактирование данных #

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

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


Работа со сложными структурами данных #

При работе со сложными вложенными структурами приходится решать вопрос по получению и изменению свойств вложенных элементов

Рассмотрим следующие структуры данных

data class AcademicClass(val id: Int, val name: String,
        val teacher: String)
data class Course(val id: Int, val name: String,
        val clasess: List<AcademicClass>)
data class Speciality(val id: Int, val name: String, 
        val courses: List<Course>)
val speciality: Speciality = ...

diagram

Используем неизменяемые структуры данных, чтобы предотвратить проблемы конкурентного доступа и модификации


Модификация структур данных #

Для получения информации по предмету необходимо найти курс в списке, найти предмет, обратиться к полям класса для получения:

val class = speciality.courses[1].classes[5]

Для редактирования потребуется выполнить несколько копирований:

val newAcademicClass = speciality.courses[1].classes[5]
    .copy(name = "Безопасность жизнедеятельности")
val newAcademicClasses = speciality.courses[1].classes.toMutableList()
    .apply { set(5, newAcademicClass) }
val newCourse = speciality.courses[1].copy(classes = newAcademicClasses)
val newCourses = speciality.courses.toMutableList()
    .apply { set(1, newCourse) }
val newSpeciality = speciality.copy(courses = newCourses)

Можно создать новую версию неизменяемых данных с нужным состоянием, остаётся только сохранить новую копию в общем хранилище приложения


Сложные структуры сложны #

Сильная вложенность структур данных несёт следующие проблемы:

  • Надо корректно указать путь к данным несколько раз
    • Необходимо написать много кода
    • Возможны технические ошибки
  • Код, которому нужен доступ только к конечным данным, начинает зависеть от всех промежуточных объектов, что усложняет изменение структур данных в приложении

Решение с помощью функций (операций) #

Мы можем ввести функции (или операции), позволяющие решить данные задачи:

fun getName(speciality: Speciality, courseId: Int, classId: Int): String
fun setName(name: String, speciality: Speciality,
            courseId: Int, classId: Int): Speciality

Код будет зависеть только от класса Speciality и одной из указанных функций


Моделирование связных списков #

В рамках приложения данные зачастую представлены в виде списков данных: список групп, список студентов, список предметов

Элементы списков зачастую связаны с объектами из других списков

При моделировании связей между объектами в приложении кажется удобным подход с хранением ссылок на связные объекты:

data class Course(
    val id: Int,
    val name: String,
    val clasess: List<AcademicClass>,
)
  • Эта структура оптимизирована под задачу получения всех предметов курса
  • Структура не оптимизирована для других задач, например на поиск списка предметов, которые ведёт один преподаватель

Подход к моделированию списков данных #

  • Каждый элемент хранится только в своём хранилище
  • Для связи элемента из одного списка с элементом из другого списка используется не ссылка на объект, а уникальный идентификатор элемента
  • Хранилище оптимизируется для получения элемента по идентификатору

В примере выше:

  • У сущности предмет добавляется идентификатор курса, в которой он преподаётся
  • У курса добавляется идентификатор специальности

Подход применим не только для списков данных, но и для связи один-к-одному


Модели классов и курсов #

Оригинальные структуры данных

data class AcademicClass(val id: Int, val name: String,
        val teacher: String)
data class Course(val id: Int, val name: String,
        val clasess: List<AcademicClass>)
data class Speciality(val id: Int, val name: String,
        val courses: List<Course>)

Модифицированные структуры данных

data class AcademicClass(val id: Int, val name: String,
        val teacher: String, val courseId: Int)
data class Course(val id: Int, val name: String, val specialityId: Int)
data class Speciality(val id: Int, val name: String)

Хранилище для предметов:

class AcademicClassStorage() {
    fun fetch(classId: Int): AcademicClass? { ... }
    fun list(): List<AcademicClass> { ... }
    fun add(academicClass: Class): Int { ... }
    fun update(academicClass: Class) { ... }
    fun remove(classId: Int) { ... }
}

Изменение данных хранилищ #

Для выполнения операций над несколькими списками вводите операции, которые обращаются с несколькими хранилищами

diagram


Операция над данными #

Операции редактирования #

  • Операции редактирования реализуются очень просто, т.к. достаточно иметь только ссылку на соответствующее хранилище
  • Для обеспечения корректности ссылок можно передать ссылки на соответствующие хранилища, с помощью которых проверять наличие данных по указанным ссылкам

Операции извлечения данных #

  • Хранятся ссылки на хранилища, из которых необходимо извлечь данные
  • Результат работы — наборы данных, в структурах данных, наиболее удобных для их обработки на стороне получателя

Например для задачи поиска предметов, преподаваемых конкретным преподавателем

data class TeacherClasess(val teacher: String,
    val classes: List<AcademicClass>) 
class FindTeacherClasses(private val academicClassStorage) {
    fun findByTeacher(teacher: String): TeacherClasses
}

© A. M. Васильев, 2024, CC BY-SA 4.0, andrey@crafted.su