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

Продолжаем изучать шаблоны программирования или шаблоны проектирования на языке Swift на реальных примерах. В предыдущей статье Шаблоны программирования на Swift: Factory мы рассмотрели шаблон Factory, а в данной статье мы будем разбираться с шаблоном Абстрактная Фабрика —  Abstract Factory

 Abstract Factory

Абстрактная фабрика – еще один очень популярный паттерн, который как и в названии так и в реализации слегка похож на фабричный метод.

Итак, что же делает абстрактная фабрика: Абстрактная фабрика дает простой интерфейс для создания объектов которые принадлежат к тому или иному сеймейству объектов.

Отличия от фабричного метода:

  1. Фабричный метод порождает объекты одного и того же типа, Абстрактная фабрика же может создавать независимые объекты
  2. Чтобы добавить новый тип объекта–надо поменять интерфейс фабрики, в фабричном же методе легко просто поменять внутренности метода, который ответственный за порождение объектов.

Давайте представим ситуацию: у нас есть две фабрики по производству 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».


В следующей статье

Метки:

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

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

*

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