 
                Компоновщик на Swift
Компоновщик — это структурный паттерн, который позволяет создавать дерево объектов и работать с ним так же, как и с единичным объектом.
Компоновщик давно стал синонимом всех задач, связанных с построением дерева объектов. Все операции компоновщика основаны на рекурсии и «суммировании» результатов на ветвях дерева.
Сложность:
Популярность:
Применимость: Паттерн Компоновщик встречается в любых задачах, которые связаны с построением дерева. Самый простой пример — составные элементы GUI, которые тоже можно рассматривать как дерево.
Признаки применения паттерна: Если из объектов строится древовидная структура, и со всеми объектами дерева, как и с самим деревом работают через общий интерфейс.
Концептуальный пример
Этот пример показывает структуру паттерна Компоновщик, а именно — из каких классов он состоит, какие роли эти классы выполняют и как они взаимодействуют друг с другом.
После ознакомления со структурой, вам будет легче воспринимать второй пример, который рассматривает реальный случай использования паттерна в мире Swift.
Example.swift: Пример структуры паттерна
import XCTest
/// Базовый класс Компонент объявляет общие операции как для простых, так и для
/// сложных объектов структуры.
protocol Component {
    /// При необходимости базовый Компонент может объявить интерфейс для
    /// установки и получения родителя компонента в древовидной структуре. Он
    /// также может предоставить  некоторую реализацию по умолчанию для этих
    /// методов.
    var parent: Component? { get set }
    /// В некоторых случаях целесообразно определить операции управления
    /// потомками прямо в базовом классе Компонент. Таким образом, вам не нужно
    /// будет предоставлять  конкретные классы компонентов клиентскому коду,
    /// даже во время сборки дерева объектов. Недостаток такого подхода в том,
    /// что эти методы будут пустыми для компонентов уровня листа.
    func add(component: Component)
    func remove(component: Component)
    /// Вы можете предоставить метод, который позволит клиентскому коду понять,
    /// может ли компонент иметь вложенные объекты.
    func isComposite() -> Bool
    /// Базовый Компонент может сам реализовать некоторое поведение по умолчанию
    /// или поручить это конкретным классам.
    func operation() -> String
}
extension Component {
    func add(component: Component) {}
    func remove(component: Component) {}
    func isComposite() -> Bool {
        return false
    }
}
/// Класс Лист представляет собой конечные объекты структуры.  Лист не может
/// иметь вложенных компонентов.
///
/// Обычно объекты Листьев выполняют фактическую работу, тогда как объекты
/// Контейнера лишь делегируют работу своим подкомпонентам.
class Leaf: Component {
    var parent: Component?
    func operation() -> String {
        return "Leaf"
    }
}
/// Класс Контейнер содержит сложные компоненты, которые могут иметь вложенные
/// компоненты. Обычно объекты Контейнеры делегируют фактическую работу своим
/// детям, а затем «суммируют» результат.
class Composite: Component {
    var parent: Component?
    /// Это поле содержит поддерево компонентов.
    private var children = [Component]()
    /// Объект контейнера может как добавлять компоненты в свой список вложенных
    /// компонентов, так и удалять их, как простые, так и сложные.
    func add(component: Component) {
        var item = component
        item.parent = self
        children.append(item)
    }
    func remove(component: Component) {
        // ...
    }
    func isComposite() -> Bool {
        return true
    }
    /// Контейнер выполняет свою основную логику особым образом. Он проходит
    /// рекурсивно через всех своих детей, собирая и суммируя их результаты.
    /// Поскольку потомки контейнера передают эти вызовы своим потомкам и так
    /// далее,  в результате обходится всё дерево объектов.
    func operation() -> String {
        let result = children.map({ $0.operation() })
        return "Branch(" + result.joined(separator: " ") + ")"
    }
}
class Client {
    /// Клиентский код работает со всеми компонентами через базовый интерфейс.
    static func someClientCode(component: Component) {
        print("Result: " + component.operation())
    }
    /// Благодаря тому, что операции управления потомками объявлены в базовом
    /// классе Компонента, клиентский код может работать как с простыми, так и
    /// со сложными компонентами.
    static func moreComplexClientCode(leftComponent: Component, rightComponent: Component) {
        if leftComponent.isComposite() {
            leftComponent.add(component: rightComponent)
        }
        print("Result: " + leftComponent.operation())
    }
}
/// Давайте посмотрим как всё это будет работать.
class CompositeConceptual: XCTestCase {
    func testCompositeConceptual() {
        /// Таким образом, клиентский код может поддерживать простые компоненты-
        /// листья...
        print("Client: I've got a simple component:")
        Client.someClientCode(component: Leaf())
        /// ...а также сложные контейнеры.
        let tree = Composite()
        let branch1 = Composite()
        branch1.add(component: Leaf())
        branch1.add(component: Leaf())
        let branch2 = Composite()
        branch2.add(component: Leaf())
        branch2.add(component: Leaf())
        tree.add(component: branch1)
        tree.add(component: branch2)
        print("\nClient: Now I've got a composite tree:")
        Client.someClientCode(component: tree)
        print("\nClient: I don't need to check the components classes even when managing the tree:")
        Client.moreComplexClientCode(leftComponent: tree, rightComponent: Leaf())
    }
}
Output.txt: Результат выполнения
Client: I've got a simple component:
Result: Leaf
Client: Now I've got a composite tree:
Result: Branch(Branch(Leaf Leaf) Branch(Leaf Leaf))
Client: I don't need to check the components classes even when managing the tree:
Result: Branch(Branch(Leaf Leaf) Branch(Leaf Leaf) Leaf)
Пример из реальной жизни
Example.swift: Пример из реальной жизни
import UIKit
import XCTest
protocol Component {
    func accept<T: Theme>(theme: T)
}
extension Component where Self: UIViewController {
    func accept<T: Theme>(theme: T) {
        view.accept(theme: theme)
        view.subviews.forEach({ $0.accept(theme: theme) })
    }
}
extension UIView: Component {}
extension UIViewController: Component {}
extension Component where Self: UIView {
    func accept<T: Theme>(theme: T) {
        print("\t\(description): has applied \(theme.description)")
        backgroundColor = theme.backgroundColor
    }
}
extension Component where Self: UILabel {
    func accept<T: LabelTheme>(theme: T) {
        print("\t\(description): has applied \(theme.description)")
        backgroundColor = theme.backgroundColor
        textColor = theme.textColor
    }
}
extension Component where Self: UIButton {
    func accept<T: ButtonTheme>(theme: T) {
        print("\t\(description): has applied \(theme.description)")
        backgroundColor = theme.backgroundColor
        setTitleColor(theme.textColor, for: .normal)
        setTitleColor(theme.highlightedColor, for: .highlighted)
    }
}
protocol Theme: CustomStringConvertible {
    var backgroundColor: UIColor { get }
}
protocol ButtonTheme: Theme {
    var textColor: UIColor { get }
    var highlightedColor: UIColor { get }
    /// other properties
}
protocol LabelTheme: Theme {
    var textColor: UIColor { get }
    /// other properties
}
/// Button Themes
struct DefaultButtonTheme: ButtonTheme {
    var textColor = UIColor.red
    var highlightedColor = UIColor.white
    var backgroundColor = UIColor.orange
    var description: String { return "Default Buttom Theme" }
}
struct NightButtonTheme: ButtonTheme {
    var textColor = UIColor.white
    var highlightedColor = UIColor.red
    var backgroundColor = UIColor.black
    var description: String { return "Night Buttom Theme" }
}
/// Label Themes
struct DefaultLabelTheme: LabelTheme {
    var textColor = UIColor.red
    var backgroundColor = UIColor.black
    var description: String { return "Default Label Theme" }
}
struct NightLabelTheme: LabelTheme {
    var textColor = UIColor.white
    var backgroundColor = UIColor.black
    var description: String { return "Night Label Theme" }
}
class CompositeRealWorld: XCTestCase {
    func testCompositeRealWorld() {
        print("\nClient: Applying 'default' theme for 'UIButton'")
        apply(theme: DefaultButtonTheme(), for: UIButton())
        print("\nClient: Applying 'night' theme for 'UIButton'")
        apply(theme: NightButtonTheme(), for: UIButton())
        print("\nClient: Let's use View Controller as a composite!")
        /// Night theme
        print("\nClient: Applying 'night button' theme for 'WelcomeViewController'...")
        apply(theme: NightButtonTheme(), for: WelcomeViewController())
        print()
        print("\nClient: Applying 'night label' theme for 'WelcomeViewController'...")
        apply(theme: NightLabelTheme(), for: WelcomeViewController())
        print()
        /// Default Theme
        print("\nClient: Applying 'default button' theme for 'WelcomeViewController'...")
        apply(theme: DefaultButtonTheme(), for: WelcomeViewController())
        print()
        print("\nClient: Applying 'default label' theme for 'WelcomeViewController'...")
        apply(theme: DefaultLabelTheme(), for: WelcomeViewController())
        print()
    }
    func apply<T: Theme>(theme: T, for component: Component) {
        component.accept(theme: theme)
    }
}
class WelcomeViewController: UIViewController {
    class ContentView: UIView {
        var titleLabel = UILabel()
        var actionButton = UIButton()
        override init(frame: CGRect) {
            super.init(frame: frame)
            setup()
        }
        required init?(coder decoder: NSCoder) {
            super.init(coder: decoder)
            setup()
        }
        func setup() {
            addSubview(titleLabel)
            addSubview(actionButton)
        }
    }
    override func loadView() {
        view = ContentView()
    }
}
/// Let's override a description property for the better output
extension WelcomeViewController {
    open override var description: String { return "WelcomeViewController" }
}
extension WelcomeViewController.ContentView {
    override var description: String { return "ContentView" }
}
extension UIButton {
    open override var description: String { return "UIButton" }
}
extension UILabel {
    open override var description: String { return "UILabel" }
}
Output.txt: Результат выполнения
Client: Applying 'default' theme for 'UIButton'
UIButton: has applied Default Buttom Theme
Client: Applying 'night' theme for 'UIButton'
UIButton: has applied Night Buttom Theme
Client: Let's use View Controller as a composite!
Client: Applying 'night button' theme for 'WelcomeViewController'...
ContentView: has applied Night Buttom Theme
UILabel: has applied Night Buttom Theme
UIButton: has applied Night Buttom Theme