Шаблоны программирования на Swift: Abstract Factory
Продолжаем изучать шаблоны программирования или шаблоны проектирования на языке Swift на реальных примерах. В предыдущей статье Шаблоны программирования на Swift: Factory мы рассмотрели шаблон Factory, а в данной статье мы будем разбираться с шаблоном Абстрактная Фабрика — Abstract Factory
Abstract Factory
Абстрактная фабрика – еще один очень популярный паттерн, который как и в названии так и в реализации слегка похож на фабричный метод.
Итак, что же делает абстрактная фабрика: Абстрактная фабрика дает простой интерфейс для создания объектов которые принадлежат к тому или иному сеймейству объектов.
Отличия от фабричного метода:
- Фабричный метод порождает объекты одного и того же типа, Абстрактная фабрика же может создавать независимые объекты
- Чтобы добавить новый тип объекта–надо поменять интерфейс фабрики, в фабричном же методе легко просто поменять внутренности метода, который ответственный за порождение объектов.
Давайте представим ситуацию: у нас есть две фабрики по производству iPhone и iPad. Одна оригинальная, компании Apple, другая – хижина дядюшки Хуа. И вот, мы хотим производить эти товары: если в страны 3-го мира – то товар от дядюшки, в другие страны – товар любезно предоставлен компанией Apple.
Итак, пускай у нас есть фабрика, которая умеет производить и айпады и айфоны:
// // proSwift.ru // class IPhoneFactory { }
Естественно, нам необходимо реализовать продукты, которые фабрика будет производить:
// // proSwift.ru // class GenericIPad { var osName: String? var productName: String? var screenSize: Float? } class GenericIPhone { var osName: String? var productName: String? }
Но, продукты немного отличаются. Пускай у нас есть два типа продуктов, оригинальные Apple и продукты которые произведены трудолюбивым дядюшкой Сяо-Ляо:
// // proSwift.ru // class AppleIPhone: GenericIPhone { override init() { super.init() self.productName = "iPhone" self.osName = "iOS" } } class AppleIPad: GenericIPad { override init() { super.init() self.productName = "iPad" self.osName = "iOS" self.screenSize = 7.7 } } class ChinaPhone: GenericIPhone { override init() { super.init() self.productName = "Ляо-Фон" self.osName = "Ведроид" } } class ChinaPad: GenericIPad { override init() { super.init() self.productName = "Сяо-Пад" self.osName = "Окна-ЦЕ" self.screenSize = 12.1 } }
Разные телефоны, конечно же, производятся на различных фабриках, потому мы просто обязанны их создать! Приблизительно так должны выглядеть фабрика Apple:
// // proSwift.ru // class AppleFactory: IPhoneFactory { func getIPhone() -> GenericIPhone { let iPhone = AppleIPhone() return iPhone } func getIPad() -> GenericIPad { let iPad = AppleIPad() return iPad } }
Конечно же у нашего китайского дядюшки тоже есть своя фабрика:
// // proSwift.ru // class ChinaFactory: IPhoneFactory { func getIPhone() -> GenericIPhone { let phone = ChinaPhone() return phone } func getIPad() -> GenericIPad { let pad = ChinaPad() return pad } }
Как видим, фабрики одинаковые, а вот девайсы у них получаются разные.
Ну вот собственно и все, мы приготовили все что надо для демонстрации! Теперь, давайте напишем небольшой метод который будет возвращать нам фабрику которую мы хотим (кстати, тут фабричный метод таки будет):
// // proSwift.ru // var isThirdWorld: Bool = true func getFactory() -> IPhoneFactory { if (isThirdWorld) { return ChinaFactory() } return AppleFactory() }
Теперь меняя значение переменной isThirdWorld можно получать те или иные девайсы.
// // proSwift.ru // let fac = getFactory() var iPhone = GenericIPhone() var iPad = GenericIPad() if let factory = fac as? ChinaFactory { iPhone = factory.getIPhone() iPad = factory.getIPad() } if let factory = fac as? AppleFactory { iPhone = factory.getIPhone() iPad = factory.getIPad() } print("iPad: \(iPad.productName!), os name: \(iPad.osName!), screensize: \(iPad.screenSize!)") print("iPhone: \(iPhone.productName!), os name: \(iPhone.osName!)")
Ну и проверим что мы там понаписали…
// // proSwift.ru // let fac = getFactory() var iPhone = GenericIPhone() var iPad = GenericIPad() if let factory = fac as? ChinaFactory { iPhone = factory.getIPhone() iPad = factory.getIPad() } if let factory = fac as? AppleFactory { iPhone = factory.getIPhone() iPad = factory.getIPad() } print("iPad: \(iPad.productName!), os name: \(iPad.osName!), screensize: \(iPad.screenSize!)") print("iPhone: \(iPhone.productName!), os name: \(iPhone.osName!)") isThirdWorld = false let fac1 = getFactory() if let factory = fac1 as? ChinaFactory { iPhone = factory.getIPhone() iPad = factory.getIPad() } if let factory = fac1 as? AppleFactory { iPhone = factory.getIPhone() iPad = factory.getIPad() } print("iPad: \(iPad.productName!), os name: \(iPad.osName!), screensize: \(iPad.screenSize!)")
Консоль:
iPad: Сяо-Пад, os name: Окна-ЦЕ, screensize: 12.1 iPhone: Ляо-Фон, os name: Ведроид iPad: iPad, os name: iOS, screensize: 7.7 iPhone: iPhone, os name: iOS
Итак:
Паттерн Абстрактная фабрика используется для обеспечения клиента набором родственных или зависимых объектов. «Семья» объектов, созданных на заводе-изготовителе определяются во время выполнения.
// // proSwift.ru // // https://github.com/ochococo/Design-Patterns-In-Swift/blob/master/source/creational/abstract_factory.swift import Foundation protocol Decimal { func stringValue() -> String // factory static func make(string : String) -> Decimal } typealias NumberFactory = (String) -> Decimal // Number implementations with factory methods struct NextStepNumber : Decimal { private var nextStepNumber : NSNumber func stringValue() -> String { return nextStepNumber.stringValue } // factory static func make(string : String) -> Decimal { return NextStepNumber(nextStepNumber:NSNumber(longLong:(string as NSString).longLongValue)) } } struct SwiftNumber : Decimal { private var swiftInt : Int func stringValue() -> String { return "\(swiftInt)" } // factory static func make(string : String) -> Decimal { return SwiftNumber(swiftInt:(string as NSString).integerValue) } } enum NumberType { case NextStep, Swift } enum NumberHelper { static func factoryFor(type : NumberType) -> NumberFactory { switch type { case .NextStep: return NextStepNumber.make case .Swift: return SwiftNumber.make } } } let factoryOne = NumberHelper.factoryFor(.NextStep) let numberOne = factoryOne("1") print(numberOne.stringValue()) let factoryTwo = NumberHelper.factoryFor(.Swift) let numberTwo = factoryTwo("2") print(numberTwo.stringValue())
В зависимости от типа числа, переданного в NumberHelper, мы получаем функцию make соответствующего класса. Т.е. переменная factoryOne равна функции make класса NexStepNumber. Мы в эту функцию передаем строку «1», внутри функции идет преобразование и в консоль уже выводится строковые представление, полученное методом stringValue(): в консоле получаем строку «1».
Соответсвенно в factoryTwo мы имеем функцию make класса SwiftNumber, передаем в нее строку «2». После преобразований имеем на выходе в консоле строковое представление «2».
В следующей статье…
Добавить комментарий