设计模式-创建型模式 对象模版模式 对象模版模式是最简单的一种设计模式,即使用类和结构体创建对象,而不是使用零散的变量、变量数组或元组来保存信息。使用对象模版模式可以将数据与操作数据的方法封装在一起,从而隐藏接口的内部实现,松散耦合。
原型模式 原型模式是指用已有的对象作为原型,通过克隆的方式来创建新的对象,而不是通过初始化函数。这样可以将创建对象的代码隐藏,无需知道创建新的对象需要用到哪些类或结构体。当初始化的开销很大,或是想要降低对模版的依赖时,就可以使用此模式。
当使用值类型时,Swift 会自动使用原型模式。而当我们使用引用类型时,就需要让类继承 NSObject 并遵循 NSCopying 协议,实现其中的 copyWithZone
方法来定义如何复制对象。需要注意在复制对象时使用深拷贝还是浅拷贝。
单例模式 单例模式可以确保某个类型的对象在应用程序中只出现一个实例。单例模式的实现与所使用的语言密切相关。
在 Swift 中,可以使用全局常量来实现单例模式:
1 2 3 4 5 6 7 8 let globalLogger = Logger ()final class Logger { fileprivate init () { } }
Swift 的全局常量是惰性初始化的,且能保证线程安全。只有在第一次访问全局常量时才会初始化,且只初始化一次。即使在另外的线程中读取,也只会创建一个实例。
我们使用 final 关键字来修饰类,来防止子类的创建。把 init 函数前面加上 fileprivate 来阻止 Logger.swift 以外的地方的代码来创建实例。通过 let 关键字来声明对象,可以防止引用的指向被修改。这样就实现了一个单例。
当然还有更简单的实现方法:
1 2 3 4 final class Logger { static let sharedLogger = Logger () private init () {} }
Cocoa 中许多地方都使用了单例模式,比如 UIApplication 的 sharedApplication。
对象池模式 对象池模式是单例模式的一种变体,不同的是它提供了多个完全相同的对象,而非单个对象。使用时应该从对象池中取出对象,使用它完成任务后再归还给对象池。在归还以前,其他组件将不能使用它。
举例来说明,图书馆中绝大多数书都不止一本,但是范围又是有限定的。创建或复制 Book 对象并不能使图书馆的藏书量真的增多,这个时候就应该使用对象池来管理图书。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 class Pool <T > { private var data = [T ]() init (items: [T ]) { for eachItem in items { data.append(eachItem) } } func getFromPool () -> T? { var result: T? if data.count > 0 { result = data.removeAtIndex(0 ) } return result } func returnToPool (item: T) { data.append(item) } }
我们维护了一个队列来管理对象池。需要注意的是,如果程序中使用了多线程,则需要做好数据的保护。
Cocoa 中,UITableView 维护了一个 UITableViewCell 的对象池来节省内存开销。
工厂方法模式 当多个类遵循同一个协议,或是继承自同一个基类,而我们需要根据条件选择一个类来初始化对象时,就可以使用工厂方法模式。工厂方法模式同意了实现类的选取逻辑,从而避免了相关逻辑散布在整个程序中,调用组件无需了解实现类即选取实现类的过程。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 class RentalCar { fileprivate var name = "" fileprivate var passengers = 0 fileprivate init (name: String , passengers: Int ) { self .name = name self .passengers = passengers } final var name: String { get { return name } } final var passengers: Int { get { return passengers } } class func createRentalCar (passengers : Int ) -> RentalCar ? { var car: RentalCar? switch passengers { case 0 ...3 : car = Compact () case 4 ...8 : car = SUV () default : car = nil } return car } } class Compact : RentalCar { fileprivate init () { super .init (name: "Golf" , passengers: 3 ) } } class SUV : RentalCar { fileprivate init () { super .init (name: "Range Rover" , passengers: 8 ) } } class CarSelector { class func selectCar (passengers : Int ) -> String ? { return RentalCar .createRentalCar(passengers)?.name } }
抽象工厂模式 当调用组件需要一组互相协作的对象,又不需要关心这些对象的具体协作方式时,就可以使用抽象工厂模式。
当调用抽象工厂去创建对象时,抽象工厂会检查请求,然后选择一个具体工厂,使用具体工厂创建对象并返回给调用组件。
建造者模式 建造者模式用于分离对象的创建和配置。调用组件负责提供配置对象的数据,把数据传给中间人建造者,建造者再去创建对象。这样调用者就无需过多掌握其使用的对象的信息,而默认配置可以集中放置在建造者中。如果创建对象需要进行复杂的配置,就可以使用这种模式。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 class Burger { let pickles: Bool let mayo: Bool let lettuce: Bool let ketchup: Bool init (pickles: Bool , mayo: Bool , lettuce: Bool , ketchup: Bool ) { self .pickles = pickles self .mayo = mayo self .lettuce = lettuce self .ketchup = ketchup } } class BurgerBuilder { private var pickles = false private var mayo = false private var lettuce = true private var ketchup = true func setPickles (choice: Bool) { self .pickles = choice } func setMayo (choice: Bool) { self .mayo = choice } func setLettuce (choice: Bool) { self .lettuce = choice } func setKetchup (choice: Bool) { self .ketchup = choice } func buildBurger () -> Burger { return Burget (pickles: picklse, mayo: mayo, lettuce: lettuce, ketchup: ketchup) } }