Компоновщик
Напишем todo-лист на стероидах — используем паттерн "Дерево" чтобы написать модель данных будущего приложения
Что может быть популярней чем стартый-добрый todo-лист? Наверно, это самый популярный тип приложения, который создают чтобы потрогать какую-то новую технологию. Суть: можно создать задачку, переименовать её, пометить сделаной, пометить несделаной, удалить.
Но, конечно в реальной жизни такого функционала пользователям недостаточно. Давайте сделаем приложение интересней, добавим пару фич.
Усложним функционал
Прежде всего, напрашивается возможность объединения задач в группы. Чтобы юзер мог объединять задачки по какому-то признаку. Например: важные, срочные, рабочие, на неделю и т.д. Например вот так:
А если вдруг список дел перестал быть актуальным, мы могли бы закрыть все дочерние задачи на уровне группы.
Киллер-фича: группировка
А что если дать возможность объединять в группы не только одиночные задачки, но и другие группы задач? Чтобы получился этакий конструктор, где бы мы могли объявлять глобальную задачу, которая бы включала в себя ряд задач и групп задач, которые могли бы включать в себя другие группы и так далее... И в итоге мы могли бы строить целые деревья задач!
Модель данных
Главный вызов в таком приложении — создать модель данных, которая бы позволяла просто работать с бесконечной вложенностью групп задач и менеджерить их. Для этого отлично подходит паттерн Компоновщик (или дерево). Он позволяет нам одинаково взаимодействовать как с одиночными задачами, так и с группами.
Объявим интерфейс задачи. Мы можем управлять её статусом готовности и менять имя.
interface TaskInterface {
getIsCompleted: () => boolean;
setIsCompleted: (isCompleted: boolean) => void;
getName: () => string;
setName: (name: string) => void;
}
Создадим класс для одиночной задачи, который реализует этот интерфейс.
class Task implements TaskInterface {
private isCompleted: boolean;
private name: string;
constructor(name: string) {
this.isCompleted = false;
this.name = name;
}
getIsCompleted() {
return this.isCompleted;
}
setIsCompleted(isCompleted: boolean) {
this.isCompleted = isCompleted;
}
getName() {
return this.name;
}
setName(name: string) {
this.name = name;
}
}
А теперь, самое интересное. Создадим класс группы задач, который будет реализовывать интерфейс задачи и меть пару дополнительных методов add и remove для того, чтобы управлять дочерним списком задач.
class TasksGroup implements TaskInterface {
private tasks: Set<Task | TasksGroup>;
private name: string;
constructor(name: string) {
this.tasks = new Set();
this.name = name;
}
add(task: Task) {
this.tasks.add(task);
}
remove(task: Task) {
this.tasks.delete(task);
}
getIsCompleted() {
if (this.tasks.size === 0) return true;
return Array.from(this.tasks).every((task) => {
return task.getIsCompleted();
});
}
setIsCompleted(isCompleted: boolean) {
Array.from(this.tasks).forEach((task) => {
task.setIsCompleted(isCompleted);
});
}
getName() {
return this.name;
}
setName(name: string) {
this.name = name;
}
}
Создадим модель данных
Используем классы для создания групп и задач.
// создадим певую группу задач
const playCat = new Task('Поиграть с котом');
const washDish = new Task('Помыть посуду');
const homeGroup = new TasksGroup('Домашние дела');
homeGroup.add(playCat);
homeGroup.add(washDish);
// создадим вторую
const dryCleaning = new Task('Забрать вещи из химчистки');
const pharmacyOrder = new Task('Забрать заказ из аптеки');
const importantGroup = new TasksGroup('Важные дела');
importantGroup.add(dryCleaning);
importantGroup.add(importantGroup);
// объединим их в общую вместе с другими задачами
const gym = new Task('Сходить в спортзал');
const article = new Task('Написать статью в блог');
const weeklyGroup = new TasksGroup('Дела на неделю');
weeklyGroup.add(gym);
weeklyGroup.add(article);
weeklyGroup.add(homeGroup);
weeklyGroup.add(importantGroup);
// пометим всю ветку домашних задач как выполненную
homeGroup.setIsCompleted(true);
Итог
Паттерн Компоновщик отлично подходит для приложений, где мы работает с древовидной структурой данных и когда необходимо реализовать одинаковую логику работы как с одиночными объектами, так и с их группами.
Комментарии