Что это такое
Паттерн Composite (компоновщик) работает с отдельными объектами и группами объектов через один и тот же интерфейс. У файла есть размер; у папки, полной файлов (и других папок), размер тоже есть — поэтому оба реализуют Size(), и код, который складывает размеры, никогда не спрашивает, что именно он держит.
Компоновщики естественно образуют деревья: группы содержат листья и другие группы, на любую глубину.
Пример: файлы и папки
У интерфейса Item один метод. Файл отвечает своим собственным размером; папка спрашивает всё, что внутри, и складывает — рекурсия получается сама собой.
Один интерфейс для листа и для группы
root.Size() обходит всё дерево без единой проверки типа: папке всё равно, файл ли это на 4 КБ или другая папка, прячущая ещё десяток.
Вывод: программа на Go и захваченный вывод
package main
import "fmt"
type Item interface {
Size() int
}
type File struct {
name string
size int
}
func (f File) Size() int { return f.size }
type Folder struct {
name string
items []Item
}
// Размер папки — это сумма всего, что внутри: файлов или папок.
func (f Folder) Size() int {
total := 0
for _, item := range f.items {
total += item.Size()
}
return total
}
func main() {
docs := Folder{name: "docs", items: []Item{
File{name: "notes.txt", size: 4},
File{name: "plan.md", size: 6},
}}
root := Folder{name: "root", items: []Item{
File{name: "readme.md", size: 2},
docs,
}}
fmt.Println("размер docs:", docs.Size(), "КБ")
fmt.Println("размер root:", root.Size(), "КБ")
}
Когда его применять
Применяйте для древовидных данных, где группы и отдельные элементы отвечают на одни и те же вопросы — файловые системы, меню с подменю, разметка интерфейса, организационные структуры.
Не применяйте для плоских списков. Срезу с циклом никакой паттерн не нужен.
Плюсы и минусы
Плюсы: клиентский код остаётся плоским и простым; деревья любой глубины достаются бесплатно; новые типы листьев или групп подключаются через тот же интерфейс.
Минусы: общий интерфейс может навязывать листьям неуместные методы (что такое «добавить потомка» у файла?); слишком общие компоновщики трудно ограничивать.