Редактирование данных #
Васильев Андрей Михайлович, 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 = ...
Используем неизменяемые структуры данных, чтобы предотвратить проблемы конкурентного доступа и модификации
Модификация структур данных #
Для получения информации по предмету необходимо найти курс в списке, найти предмет, обратиться к полям класса для получения:
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) { ... }
}
Изменение данных хранилищ #
Для выполнения операций над несколькими списками вводите операции, которые обращаются с несколькими хранилищами
Операция над данными #
Операции редактирования #
- Операции редактирования реализуются очень просто, т.к. достаточно иметь только ссылку на соответствующее хранилище
- Для обеспечения корректности ссылок можно передать ссылки на соответствующие хранилища, с помощью которых проверять наличие данных по указанным ссылкам
Операции извлечения данных #
- Хранятся ссылки на хранилища, из которых необходимо извлечь данные
- Результат работы — наборы данных, в структурах данных, наиболее удобных для их обработки на стороне получателя
Например для задачи поиска предметов, преподаваемых конкретным преподавателем
data class TeacherClasess(val teacher: String,
val classes: List<AcademicClass>)
class FindTeacherClasses(private val academicClassStorage) {
fun findByTeacher(teacher: String): TeacherClasses
}