Что это такое
Паттерн Composite работает с отдельными объектами и группами объектов через один и тот же интерфейс. У файла есть размер; у папки, полной файлов (и папок), тоже есть размер — поэтому оба реализуют size(), и код, который суммирует размеры, никогда не спрашивает, что именно он держит.
Композиты естественно образуют деревья: группы содержат листья и другие группы, на любую глубину.
Пример: файлы и папки
У интерфейса Item один метод. FileItem отвечает своим собственным размером; Folder спрашивает всё, что внутри, и складывает — рекурсия получается сама собой.
Один интерфейс для листа и группы
root.size() обходит всё дерево без единой проверки типа: папке всё равно, является ли элемент файлом на 4 КБ или другой папкой, которая прячет ещё десять. Папка docs отвечает 10, а root отвечает 12 — свой файл на 2 КБ плюс всё, что внутри docs.
Вывод: программа на TypeScript и захваченный вывод
interface Item {
size(): number
}
// Лист: он просто знает свой собственный размер.
class FileItem implements Item {
name: string
private kb: number
constructor(name: string, kb: number) {
this.name = name
this.kb = kb
}
size(): number {
return this.kb
}
}
class Folder implements Item {
name: string
private items: Item[]
constructor(name: string, items: Item[]) {
this.name = name
this.items = items
}
// Размер папки — это сумма всего, что внутри: файлов или папок.
size(): number {
let total = 0
for (const item of this.items) {
total += item.size()
}
return total
}
}
const docs = new Folder("docs", [
new FileItem("notes.txt", 4),
new FileItem("plan.md", 6)
])
const root = new Folder("root", [
new FileItem("readme.md", 2),
docs
])
console.log("размер docs:", docs.size(), "КБ")
console.log("размер root:", root.size(), "КБ")
Когда использовать
Используйте для древовидных данных, где группы и отдельные элементы отвечают на одни и те же вопросы — файловые системы, меню с подменю, разметка интерфейса, организационные структуры.
Не используйте для плоских списков. Массиву с циклом никакой паттерн не нужен.
Плюсы и минусы
Плюсы: клиентский код остаётся простым и плоским; деревья любой глубины получаются бесплатно; новые классы листьев или групп подключаются к тому же интерфейсу.
Минусы: общий интерфейс может навязать листьям неудобные методы (что такое «добавить потомка» у файла?); слишком общие композиты трудно ограничить.