Шаблоны программирования на Swift: Composite

dp_composite-proSwift_ru

 

В этой статье мы разберем шаблон программирования Компоновщик (Composite) на языке программирования Swift в среде разработки Xcode.

И как обычно, разбираем то что  написано в книге:

Composite

Вы задумывались как много в нашей жизни древовидных структур? Начиная собственно с самих деревьев и заканчивая структурами компаний. Да ладно, даже,  компаний – целые страны используют древовидные структуры чтобы построить власть.

Во главе компании или страны частенько стоит один человек, у него есть с 10 помошников. У них тоже есть с десяток помошников, и так далее… Если нарисовать их отношения на листе бумаги – увидим дерево!
Очень часто, и мы используем такие типы даных, которые лучше всего храняться в древовидной структуре. Возьмите к примеру стандартный UI: в начале у нас есть View, в нем находяться Subview, в которых могут быть или другие View, или все такие компоненты. Та же самая структура:)

Именно для хранения таких типов данных, а вернее их организации, используется паттерн – Composite или Компоновщик.

Когда использовать такой паттерн?

Собственно когда вы работаете с древовидными типами данных, или хотите отобразить иерархию даных таким образом.
Давайте разберем более детально структуру:
В начале всегда есть контейнер в котором находятся все остальные объекты. Контейнер может хранить как другие контейнеры – ветки нашего дерева, так и объекты которые контейнерами не являюстся – листья нашего дерева. Не сложно представить, что контейнеры второго уровня могут хранить как другие контейнеры, так и листья.
Давайте пример!
Начнем с создания протокола для наших объектов:

//
// proSwift.ru
//


protocol CompositeObjectProtocol {
    func getData() -> String
    func addComponent(aComponent: CompositeObjectProtocol)
}

class Leaf: CompositeObjectProtocol {
    var leafValue: String = ""
    
    func getData() -> String {
        return "Leaf value: \(leafValue)"
    }
    
    func addComponent(aComponent: CompositeObjectProtocol) {
        print("Это листок дерева. К нему добавить ветку не могу")
    }
}

Как видим наш объект не может добавлять себе детей (ну он же не контейнер:) ), и может возвращать свое значение с помощью метода getData.

Теперь нам очень необходим контейнер:

//
// proSwift.ru
//

class Container: CompositeObjectProtocol {
    var components = [CompositeObjectProtocol]()
    
    func addComponent(aComponent: CompositeObjectProtocol) {
        self.components.append(aComponent)
    }
    
    func getData() -> String {
        var valueToReturn = "\n<ContainerValues>\n"
        
        for obj in components {
            valueToReturn.appendContentsOf(obj.getData())
        }
        
        valueToReturn.appendContentsOf("\n</ContainerValues>")
        
        return valueToReturn
    }
}

Как видим, наш контейнер может добавлять в себя детей, которые могут быть как типа Container так и типа Leaf. Метод getData бегает по всем объектам в массиве components, и вызывает тот же самый метод в детях. Вот собственно и все.
Теперь, конечно же пример:

//
// proSwift.ru
//

let rootContainer = Container()
let leaf1 = Leaf()
leaf1.leafValue = "level1 value"
rootContainer.addComponent(leaf1)

let firstLevelContainer1 = Container()
let leaf2 = Leaf()
leaf2.leafValue = "level2 value1"
firstLevelContainer1.addComponent(leaf2)
rootContainer.addComponent(firstLevelContainer1)

let firstLevelContainer2 = Container()
let leaf3 = Leaf()
leaf3.leafValue = "level2 value2"
firstLevelContainer2.addComponent(leaf3)
rootContainer.addComponent(firstLevelContainer2)

print(rootContainer.getData())

Лог выглядит следующим образом:

<ContainerValues>
Leaf value: level1 value
<ContainerValues>
Leaf value: level2 value1
</ContainerValues>
<ContainerValues>
Leaf value: level2 value2
</ContainerValues>
</ContainerValues>

В корневом контейнере содержится один лист и две ветки, которые содержат по одному листу. Именно это и отражает наш лог.

Пример с  GitHub

Шаблон компоновщик или Composite используется для создания иерархической, рекурсивной древовидной структуры связанных объектов, где любой элемент структуры может быть доступен и используется стандартным образом.

//
// proSwift.ru
//
// https://github.com/ochococo/Design-Patterns-In-Swift/blob/master/source/structural/composite.swift

protocol Shape {
    func draw(fillColor: String)
}

// Leafs

class Square : Shape {
    func draw(fillColor: String) {
        print("Drawing a Square with color \(fillColor)")
    }
}

class Circle : Shape {
    func draw(fillColor: String) {
        print("Drawing a circle with color \(fillColor)")
    }
}

// Composite

class Whiteboard : Shape {
    lazy var shapes = [Shape]()
    
    init(_ shapes:Shape...) {
        self.shapes = shapes
    }
    
    func draw(fillColor:String) {
        for shape in self.shapes {
            shape.draw(fillColor)
        }
    }
}

// ### Usage:

var whiteboard = Whiteboard(Circle(), Square())
whiteboard.draw("Red")

Код не сложный: протокол — фигура, два варианта листов — квадрат и круг, и компановщик — белый лист, который содержит массив фигур. При рисовании мы пробегаемся по всем объектам из этого массива и вызываем метод рисования для каждой фигуры.

Лог в консоль:

Drawing a circle with color Red
Drawing a Square with color Red

Метки:

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

*

Этот сайт использует Akismet для борьбы со спамом. Узнайте как обрабатываются ваши данные комментариев.