Шаблоны программирования на Swift: Decorator
В этой статье мы разберем шаблон программирования Декоратор (Decorator) на языке программирования Swift в среде разработки Xcode.
Decorator
Класный пример декоратора – различные чехлы для новых телефонов. Для начала у нас есть телефон. Но так как телефон дорогой, мы будем очень счастливы если он не разобьется при любом падении – потому мы покупаем чехол для него. То есть, к уже существующему предмету мы добавили функционал защиты от падения. Ну еще мы взяли стильный чехол – теперь наш телефон еще и выглядит отлично. А потом мы докупили съемный объектив, с помощью которого можно делать фотографии с эффектом “рыбьего глаза”. Декорировали наш телефон дополнительным функционалом:)
Вот, приблизительно так выглядит реально описание паттерна декоратор. Теперь описание GoF:
Декоратор добавляет некий функционал уже существующему объекту.
Когда использовать этот паттерн:
1. Вы хотите добавить определенному объекту дополнительные возможности, при этом не задевая и не меняя других объектов
2. Дополнительные возможности класса – опциональны
Радость Swift в данном случае – это использование расширений extension. Я не буду детально описывать расширения в этой статье, но в двух словах все же расскажу: Расширения – это возможность расширить любой объект дополнительным функционалом без использования механизма наследования. Давайте возьмем супер простой пример – декорирование Cocoa классов. К примеру добавим новый метод для объекта NSDate:
// // proSwift.ru // extension NSDate { func convertDateToString() -> String { let formatter = NSDateFormatter() formatter.dateFormat = "dd-0M-yyyy" return formatter.stringFromDate(self) } }
Как видим наше расширение определяет только один метод “convertDateToString”, который дату форматирует в текстовый формат
Собственно, это все.
// // proSwift.ru // let dateNow = NSDate() print("Сейчас: \(dateNow.convertDateToString())")
Лог выглядит ожидаемо:
Сейчас: 25-07-2016
Пример с GitHub
Шаблон декоратор используется для расширения или изменения функциональности объектов во время выполнения программы, обернув их в объект класса декоратора. Это обеспечивает гибкую альтернативу наследованию для изменения поведения.
// // proSwift.ru // // https://github.com/ochococo/Design-Patterns-In-Swift/blob/master/source/structural/decorator.swift protocol Coffee { func getCost() -> Int func getIngredients() -> String } class SimpleCoffee: Coffee { func getCost() -> Int { return 60 } func getIngredients() -> String { return "Coffee" } } class CoffeeDecorator: Coffee { private let decoratedCoffee: Coffee private let ingerientSeparator: String = ", " required init(decoratedCoffee: Coffee) { self.decoratedCoffee = decoratedCoffee } func getCost() -> Int { return decoratedCoffee.getCost() } func getIngredients() -> String { return decoratedCoffee.getIngredients() } } class Milk:CoffeeDecorator { required init(decoratedCoffee: Coffee) { super.init(decoratedCoffee: decoratedCoffee) } override func getCost() -> Int { return decoratedCoffee.getCost() + 20 } override func getIngredients() -> String { return decoratedCoffee.getIngredients() + ingerientSeparator + "Milk" } } class WhipCoffee: CoffeeDecorator { required init(decoratedCoffee: Coffee) { super.init(decoratedCoffee: decoratedCoffee) } override func getCost() -> Int { return decoratedCoffee.getCost() + 25 } override func getIngredients() -> String { return decoratedCoffee.getIngredients() + ingerientSeparator + "Whip" } } var someCoffee = SimpleCoffee() print("Стоимость напитка: \(someCoffee.getCost()), а ингредиенты: \(someCoffee.getIngredients())") let milkCoffee = Milk(decoratedCoffee: someCoffee) print("Стоимость напитка: \(milkCoffee.getCost()), а ингредиенты: \(milkCoffee.getIngredients())") let premiumCoffee = WhipCoffee(decoratedCoffee: milkCoffee) print("Стоимость напитка: \(premiumCoffee.getCost()), а ингредиенты: \(premiumCoffee.getIngredients())")
Напитки, которые унаследованы от кофейного декоратора имеют свои стоимость и ингредиенты.
—
У класса CoffeeDecorator переменные decoratedCoffee: Coffee и ingerientSeparator: String = «, » должны быть public, иначе не взлетит